diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 00000000..66ba3ad2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,38 @@ +name: Report a bug +description: | + Create a bug report to help us improve Zenoh. +title: "[Bug] " +labels: ["bug"] +body: + - type: textarea + id: summary + attributes: + label: "Describe the bug" + description: | + A clear and concise description of the expected behaviour and what the bug is. + placeholder: | + E.g. zenoh peers can not automatically establish a connection. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: To reproduce + description: "Steps to reproduce the behavior:" + placeholder: | + 1. Start a subscriber "..." + 2. Start a publisher "...." + 3. See error + validations: + required: true + - type: textarea + id: system + attributes: + label: System info + description: "Please complete the following information:" + placeholder: | + - Platform: [e.g. Ubuntu 20.04 64-bit] + - CPU [e.g. AMD Ryzen 3800X] + - Zenoh version/commit [e.g. 6f172ea985d42d20d423a192a2d0d46bb0ce0d11] + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..0c05a054 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Ask a question + url: https://github.com/eclipse-zenoh/roadmap/discussions/categories/zenoh + about: Open to the Zenoh community. Share your feedback with the Zenoh team. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 00000000..811540e4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,26 @@ +name: Request a feature +description: | + Suggest a new feature specific to this repository. NOTE: for generic Zenoh ideas use "Ask a question". +body: + - type: markdown + attributes: + value: | + **Guidelines for a good issue** + + *Is your feature request related to a problem?* + A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + + *Describe the solution you'd like* + A clear and concise description of what you want to happen. + + *Describe alternatives you've considered* + A clear and concise description of any alternative solutions or features you've considered. + + *Additional context* + Add any other context about the feature request here. + - type: textarea + id: feature + attributes: + label: "Describe the feature" + validations: + required: true diff --git a/Makefile b/.github/release.yml similarity index 54% rename from Makefile rename to .github/release.yml index d8835a3a..c4022026 100644 --- a/Makefile +++ b/.github/release.yml @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, 2020 ADLINK Technology Inc. +# 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 @@ -9,18 +9,17 @@ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 # # Contributors: -# ADLINK zenoh team, +# ZettaScale Zenoh Team, # -# Minimal Makefile calling Apache Maven - -.PHONY: all install clean - -all: - mvn install - -install: all - @echo "" - -clean: - mvn clean +changelog: + categories: + - title: New features 🎉 + labels: + - enhancement + - title: Bug fixes 🐞 + labels: + - bug + - title: Other changes + labels: + - "*" diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml deleted file mode 100644 index 1b3eec1e..00000000 --- a/.github/workflows/build-release.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Build (for all supported platform) - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - name: Set up SWIG - run: sudo apt-get install swig - - name: Build with Maven - run: mvn -B package --file pom.xml -Prelease - - name: Gather artifacts - run: | - mkdir -p artifacts/zenoh-java/examples/zenoh artifacts/zenoh-java/examples/zenoh-net - cp zenoh/target/zenoh-*.jar artifacts/zenoh-java/ - cp examples/zenoh/target/zenoh-examples-*.jar artifacts/zenoh-java/examples/zenoh/ - cp examples/zenoh-net/target/zenoh-net-examples-*.jar artifacts/zenoh-java/examples/zenoh-net/ - - name: Upload artifact - uses: actions/upload-artifact@v1.0.0 - with: - # Artifact name - name: zenoh-java - # Directory containing files to upload - path: artifacts/zenoh-java diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 8a0823c6..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Build (simple, Ubuntu only) - -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Set up JDK 1.8 - uses: actions/setup-java@v1 - with: - java-version: 1.8 - - name: Set up SWIG - run: sudo apt-get install swig - - name: Build with Maven - run: mvn -B package --file pom.xml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..af0b399c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,68 @@ +# This workflow will install Python dependencies, run tests and lint with a variety of Python versions +# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: CI + +on: + push: + branches: [ "**" ] + pull_request: + branches: [ "**" ] + schedule: + - cron: "0 6 * * 1-5" + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + ci: + name: Build on ${{ matrix.os }} + runs-on: [ "${{ matrix.os }}" ] + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, macOS-latest ] + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26 + add-to-path: false + link-to-sdk: true + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + override: true + components: rustfmt, clippy + + - name: Cargo Format + working-directory: zenoh-jni + run: cargo fmt --all --check + + - name: Clippy Check + working-directory: zenoh-jni + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Check for feature leaks + working-directory: zenoh-jni + run: cargo test --no-default-features + + - name: Build Zenoh-JNI + working-directory: zenoh-jni + run: cargo build + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Gradle Test + run: gradle jvmTest --info diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml new file mode 100644 index 00000000..b52f0bb4 --- /dev/null +++ b/.github/workflows/documentation.yml @@ -0,0 +1,32 @@ +name: Publish Documentation + +on: + release: + types: [published] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + build_doc_and_deploy: + name: Build and Deploy Documentation + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26 + add-to-path: false + link-to-sdk: true + + - name: Build doc + run: gradle dokkaHtml + + - name: Deploy doc + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./zenoh-kotlin/build/dokka/html diff --git a/.github/workflows/publish_android.yml b/.github/workflows/publish_android.yml new file mode 100644 index 00000000..5b712fd4 --- /dev/null +++ b/.github/workflows/publish_android.yml @@ -0,0 +1,58 @@ +name: Publish + +on: + release: + types: [published] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + publish_android_package: + name: Publish Android Package to Github Packages + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26 + add-to-path: false + link-to-sdk: true + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + override: true + components: rustfmt, clippy + + - name: Setup Rust toolchains + run: | + rustup target add armv7-linux-androideabi + rustup target add i686-linux-android + rustup target add aarch64-linux-android + rustup target add x86_64-linux-android + + - name: Gradle Wrapper + run: | + gradle wrapper + + - name: Validate Gradle wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Gradle Publish Android Package + uses: gradle/gradle-build-action@v2 + with: + arguments: publishAndroidReleasePublicationToGithubPackagesRepository + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/publish_jvm.yml b/.github/workflows/publish_jvm.yml new file mode 100644 index 00000000..5171de30 --- /dev/null +++ b/.github/workflows/publish_jvm.yml @@ -0,0 +1,155 @@ +name: Publish JVM + +on: + release: + types: [published] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + JNI_LIB_PATHS: jni-libs # Edit on the inner build.gradle.kts file as well. + +jobs: + builds: + name: Build for ${{ matrix.job.target }} on ${{ matrix.job.os }} + if: ${{ !(github.event.inputs.build == 'false') }} + runs-on: ${{ matrix.job.os }} + strategy: + fail-fast: false + matrix: + job: + # In order to load any added target at runtime, editing the Zenoh class under jvmMain is required. + - { target: x86_64-unknown-linux-gnu, + arch: amd64, + os: ubuntu-20.04 + } + - { + target: aarch64-unknown-linux-gnu, + arch: arm64, + os: ubuntu-20.04, + use-cross: true, + } + - { target: x86_64-apple-darwin, + arch: darwin, + os: macos-latest + } + - { target: aarch64-apple-darwin, + arch: darwin, + os: macos-latest + } + - { target: x86_64-pc-windows-msvc, + arch: win64, + os: windows-2019 + } + steps: + - name: Checkout source code + uses: actions/checkout@v4 + + - name: Install prerequisites + shell: bash + run: | + case ${{ matrix.job.target }} in + *-linux-gnu*) cargo install cargo-deb ;; + esac + + case ${{ matrix.job.target }} in + aarch64-unknown-linux-gnu) + sudo apt-get -y update + sudo apt-get -y install gcc-aarch64-linux-gnu + ;; + esac + + - name: Install Rust toolchain + run: | + rustup show + rustup target add ${{ matrix.job.target }} + + - name: Build + uses: actions-rs/cargo@v1 + with: + use-cross: ${{ matrix.job.use-cross }} + command: build + args: --release --bins --lib --features=${{ github.event.inputs.features}} --target=${{ matrix.job.target }} --manifest-path zenoh-jni/Cargo.toml + + - name: Packaging + id: package + shell: bash + run: | + TARGET=${{ matrix.job.target }} + MAIN_PKG_NAME="${GITHUB_WORKSPACE}/${TARGET}.zip" + + case ${TARGET} in + *linux*) + cd "zenoh-jni/target/${TARGET}/release/" + echo "Packaging ${MAIN_PKG_NAME}:" + zip ${MAIN_PKG_NAME} libzenoh_jni.so + cd - + echo "MAIN_PKG_NAME=${MAIN_PKG_NAME}" >> $GITHUB_OUTPUT + ;; + *apple*) + cd "zenoh-jni/target/${TARGET}/release/" + echo "Packaging ${MAIN_PKG_NAME}:" + zip ${MAIN_PKG_NAME} libzenoh_jni.dylib + cd - + echo "MAIN_PKG_NAME=${MAIN_PKG_NAME}" >> $GITHUB_OUTPUT + ;; + *windows*) + cd "zenoh-jni/target/${TARGET}/release/" + echo "Packaging ${MAIN_PKG_NAME}:" + 7z -y a "${MAIN_PKG_NAME}" zenoh_jni.dll + cd - + echo "MAIN_PKG_NAME=${MAIN_PKG_NAME}" >> $GITHUB_OUTPUT + ;; + esac + + - name: "Upload packages" + uses: actions/upload-artifact@master + with: + name: ${{ matrix.job.target }} + path: | + ${{ steps.package.outputs.MAIN_PKG_NAME }} + + publish_jvm_package: + name: Publish JVM package + needs: builds + permissions: + contents: read + packages: write + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Create resources destination + run: mkdir ${{env.JNI_LIB_PATHS}} + + - name: Download result of previous builds + uses: actions/download-artifact@v3 + with: + path: ${{env.JNI_LIB_PATHS}} + + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26 + add-to-path: false + link-to-sdk: true + + - name: Gradle Wrapper + run: | + gradle wrapper + + - name: Validate Gradle wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Gradle Publish JVM Package + uses: gradle/gradle-build-action@v2 + with: + arguments: publishJvmPublicationToGithubPackagesRepository + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..9adecf9a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,55 @@ +name: Release + +on: + release: + types: [published] + schedule: + - cron: "0 1 * * 1-5" + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + release: + name: Build on ${{ matrix.os }} + runs-on: [ "${{ matrix.os }}" ] + strategy: + fail-fast: false + matrix: + os: [ ubuntu-latest, macOS-latest ] + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-java@v3 + with: + distribution: temurin + java-version: 11 + + - uses: nttld/setup-ndk@v1 + id: setup-ndk + with: + ndk-version: r26 + add-to-path: false + link-to-sdk: true + + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + override: true + components: rustfmt, clippy + + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + + - name: Clippy Check + working-directory: zenoh-jni + run: cargo clippy --all-targets --all-features -- -D warnings + + - name: Cargo Format + working-directory: zenoh-jni + run: cargo fmt --all --check + + - name: Gradle Test + run: gradle jvmTest --info diff --git a/.gitignore b/.gitignore index 88d052ab..86cb1e5f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,21 @@ -# Maven build directory -target -*.versionsBackup +# Generated by Cargo +# will have compiled files and executables +**/target -# IDE files -.classpath -.project -.settings +# These are backup files generated by rustfmt +**/*.rs.bk -# Compiled class file -*.class +# VSCode projet direcctory +.vscode -# Log file -*.log +# CLion projet directory +.idea -# BlueJ files -*.ctxt +# Emacs temps +*~ -# Mobile Tools for Java (J2ME) -.mtj.tmp/ - -# Package Files # -*.jar -*.war -*.nar -*.ear -*.zip -*.tar.gz -*.rar - -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* +# MacOS Related +.DS_Store +/examples/build/ +/build/ +/.gradle/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1dd0b82c..aa6cb2e1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -46,6 +46,6 @@ Contact the project developers via the project's "dev" list. * https://accounts.eclipse.org/mailing-list/zenoh-dev -Or via the Gitter channel. +Or via the Discord server. -* https://gitter.im/atolab/zenoh +* https://discord.gg/vSDSpqnbkm diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index de53d693..5a82fbae 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -5,9 +5,5 @@ These are the contributors to Eclipse zenoh (the initial contributors and the co | GitHub username | Name | | --------------- | -----------------------------| -| kydos | Angelo Corsaro (ADLINK) | -| JEnoch | Julien Enoch (ADLINK) | -| OlivierHecart | Olivier Hécart (ADLINK) | -| gabrik | Gabriele Baldoni (ADLINK) | -| Mallets | Luca Cominardi (ADLINK) | -| IvanPaez | Ivan Paez (ADLINK) | +| DariusIMP | Darius Maitia (ZettaScale) | +| JEnoch | Julien Enoch (ZettaScale) | diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index d7b62c0e..00000000 --- a/Jenkinsfile +++ /dev/null @@ -1,58 +0,0 @@ -pipeline { - agent any - parameters { - gitParameter name: 'TAG', - type: 'PT_TAG', - defaultValue: 'master' - } - - stages { - stage('Native libs build (on dedicated agent)') { - agent { label 'MacMini' } - steps { - cleanWs() - checkout scm - sh ''' - env - - git log --graph --date=short --pretty=tformat:'%ad - %h - %cn -%d %s' -n 20 || true - echo $PATH - echo $JAVA_HOME - cd zenoh - mvn -Prelease generate-sources - ''' - stash includes: 'zenoh/target/generated-sources/**/*.java, zenoh/target/resources/**/*zenohc_java.*', name: 'nativeLibs' - } - } - - stage('Release build') { - tools { - maven 'apache-maven-latest' - jdk 'adoptopenjdk-hotspot-jdk8-latest' - } - steps { - cleanWs() - checkout scm - unstash 'nativeLibs' - sh ''' - ls -al zenoh/target/generated-sources/java/org/eclipse/zenoh/swig/ - ls -al zenoh/target/resources/natives/* - ls -al ~/.m2/repository - ls -al /home/jenkins/.m2 - ls -al /home/jenkins/.m2/repository - ''' - withCredentials([file(credentialsId: 'secret-subkeys.asc', variable: 'KEYRING')]) { - sh 'gpg --batch --import "${KEYRING}"' - sh 'for fpr in $(gpg --list-keys --with-colons | awk -F: \'/fpr:/ {print $10}\' | sort -u); do echo -e "5\ny\n" | gpg --batch --command-fd 0 --expert --edit-key ${fpr} trust; done' - } - sh 'mvn -Djipp -Prelease deploy' - } - } - } - - post { - success { - archiveArtifacts artifacts: 'zenoh/target/zenoh-*.jar, examples/*/target/zenoh-*.jar', fingerprint: true - } - } -} diff --git a/NOTICE.md b/NOTICE.md index a9fa77a7..7aff175a 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1,4 +1,4 @@ -# Notices for Eclipse zenoh-java +# Notices for Eclipse zenoh This content is produced and maintained by the Eclipse zenoh project. diff --git a/README.md b/README.md index 95c83405..93731528 100644 --- a/README.md +++ b/README.md @@ -1,60 +1,216 @@ -![zenoh banner](./zenoh-dragon.png) + -![Build](https://github.com/eclipse-zenoh/zenoh-java/workflows/Build%20(for%20all%20supported%20platform)/badge.svg) -[![Documentation Status](https://readthedocs.org/projects/zenoh-java/badge/?version=latest)](https://zenoh-java.readthedocs.io/en/latest/?badge=latest) -[![Gitter](https://badges.gitter.im/atolab/zenoh.svg)](https://gitter.im/atolab/zenoh?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Discussion](https://img.shields.io/badge/discussion-on%20github-blue)](https://github.com/eclipse-zenoh/roadmap/discussions) +[![Discord](https://img.shields.io/badge/chat-on%20discord-blue)](https://discord.gg/2GJ958VuHs) [![License](https://img.shields.io/badge/License-EPL%202.0-blue)](https://choosealicense.com/licenses/epl-2.0/) [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) -# Eclipse zenoh Java Client API -The Java API for [Eclipse zenoh](https://zenoh.io), based on the zenoh-c API via JNI. +# Eclipse Zenoh -:warning: **zenoh has been subject to a complete rewrite with major protocol updates between versions 0.4.2 and 0.5.0. The Java API does not yet integrate those changes and is only compatible with version 0.4.2 of the zenoh daemon and the underlying zenoh-c stack.** +The Eclipse Zenoh: Zero Overhead Pub/sub, Store/Query and Compute. -## Installation +Zenoh (pronounce _/zeno/_) unifies data in motion, data at rest and computations. It carefully blends traditional pub/sub with geo-distributed storages, queries and computations, while retaining a level of time and space efficiency that is well beyond any of the mainstream stacks. -zenoh-java is available on Maven Central. -Just add the dependency in your POM: -```xml - - org.eclipse.zenoh - zenoh - 0.4.2-M1 - +Check the website [zenoh.io](http://zenoh.io) and the [roadmap](https://github.com/eclipse-zenoh/roadmap) for more detailed information. + + +---- + +# Java Java API + + +This repository provides a Java compatible Kotlin binding based on the main [Zenoh implementation written in Rust](https://github.com/eclipse-zenoh/zenoh). + +The code relies on the Zenoh JNI native library, which written in Rust and communicates with the Kotlin layer via the Java Native Interface (JNI). + +## Documentation + +TO DO + +---- +# How to import + +## Android Android + +TO DO + +--- + +# How to build it + +## What you need + +Basically: +* Rust ([Installation guide](https://doc.rust-lang.org/cargo/getting-started/installation.html)) +* Kotlin ([Installation guide](https://kotlinlang.org/docs/getting-started.html#backend)) +* Gradle ([Installation guide](https://gradle.org/install/)) +* Android SDK ([Installation guide](https://developer.android.com/about/versions/11/setup-sdk)) + +## Android Android + +In order to use these bindings in a native Android project, what we will do is to build them as an Android NDK Library, +publishing it into Maven local for us to be able to easily import it in our project. + +It is required to have the [NDK (native development kit)](https://developer.android.com/ndk) installed, since we are going to compile Zenoh JNI for multiple +android native targets. +It can be set up by using Android Studio (go to `Preferences > Appearance & Behavior > System settings > Android SDK > SDK Tools` and tick the NDK box), +or alternatively it can be found [here](https://developer.android.com/ndk/downloads). + +The native platforms we are going to target are the following ones: +``` +- x86 +- x86_64 +- arm +- arm64 +``` + +Therefore, if they are not yet already added to the Rust toolchain, run: +```bash +rustup target add armv7-linux-androideabi; \ +rustup target add i686-linux-android; \ +rustup target add aarch64-linux-android; \ +rustup target add x86_64-linux-android +``` + +to install them. + + +So, in order to publish the library onto Maven Local, run: +```bash +gradle publishAndroidReleasePublicationToMavenLocal +``` + +This will first trigger the compilation of the Zenoh-JNI for the previously mentioned targets, and secondly will +publish the library, containing the native binaries. + +You should now be able to see the package under `~/.m2/repository/io/zenoh/zenoh-java-android/0.10.0-rc` +with the following files: +``` +zenoh-java-android-0.10.0-rc-sources.jar +zenoh-java-android-0.10.0-rc.aar +zenoh-java-android-0.10.0-rc.module +zenoh-java-android-0.10.0-rc.pom +``` + +Now the library is published on maven local, let's now see how to import it into an Android project. + +First, we need to indicate we want to look into mavenLocal for our library, so in your top level `build.gradle.kts` you need to specify +the `mavenLocal` repository: +``` +repositories { + mavenCentral() + ... + mavenLocal() // We add this line +} +``` + +Then in your app's `build.gradle.kts` filen add the dependency: +``` +implementation("io.zenoh:zenoh-java-android:0.10.0-rc") +``` + +And finally, do not forget to add the required internet permissions on your manifest! + +``` + + +``` + +And that was it! You can now import the code from the `io.zenoh` package and use it at your will. + +## JVM JVM + +To publish a library for a JVM project into Maven local, run + +```bash +gradle publishJvmPublicationToMavenLocal +``` + +This will first, trigger the compilation of Zenoh-JNI, and second publish the library into maven local, containing the native library +as a resource that will be loaded during runtime. + +:warning: The native library will be compiled against the default rustup target on your machine, so although it may work fine +for you on your desktop, the generated publication may not be working on another computer with a different operating system and/or a different cpu architecture. +This is different from Android in the fact that Android provides an in build mechanism to dynamically load native libraries depending on the CPU's architecture, while +for JVM it's not the case and that logic must be implemented. Building against multiple targets and loading them dynamically is one of our short term goals. + +Once we have published the package, we should be able to find it under `~/.m2/repository/io/zenoh/zenoh-java-jvm/0.10.0-rc`. + +Finally, in the `build.gradle.kts` file of the project where you intend to use this library, add mavenLocal to the list of repositories and add zenoh-kotlin as a dependency: + +``` +repositories { + mavenCentral() + mavenLocal() +} + +dependencies { + testImplementation(kotlin("test")) + implementation("io.zenoh:zenoh-java-jvm:0.10.0-rc") +} +``` + +## Building the documentation + +TO DO + +## Running the tests + +To run the tests, run: + +```bash +gradle jvmTest +``` + +This will compile the native library on debug mode (if not already available) and run the tests afterward against the JVM target. +Running the tests against the Android target (by using `gradle testDebugUnitTest`) is equivalent to running them against the JVM one, since they are common +tests executed locally as Unit tests. + +## Logging + +Rust logs are propagated when setting the property `zenoh.logger=debug` (using RUST_LOG=debug will result in nothing) + +For instance running the ZPub test as follows: + +```bash +gradle -Pzenoh.logger=debug ZPub +``` + +causes the logs to appear in standard output. + +The log levels are the ones from Rust: `trace`, `info`, `debug`, `error` and `warn`. + +--- + +# Examples + +You can find some examples located under the [`/examples` folder](examples). +Once we've built the project, to run them, simply run `./gradlew `. + +For instance in order to run the [ZPub](examples/kotlinExamples/src/main/kotlin/io.zenoh/ZPub.kt) example, type: + +```bash +./gradlew ZPub ``` -## Building -Requirements: +You can find more info about these examples on the [examples README file](/examples/README.md). - - [Java >= 8](http://openjdk.java.net) - - [Apache Maven >= 3.6.0](https://maven.apache.org/download.cgi) - - [SWIG](http://swig.org) - - [CMake](https://cmake.org) - - make, gcc (for zenoh-c compilation) -Optional for cross-compilation: - - Docker -To build for your current platform: -```mvn clean install``` -If zenoh-c is found in the same directory than zenoh-java, the build will copy its sources and compile it. -Otherwise, the build will clone the [zenoh-c](https://github.com/eclipse-zenoh/zenoh-c) repository and compile it. -Note that this Maven build offers profiles in addition of the default one: +---- - - ```mvn -Pdebug clean install``` +# :warning: Considerations & Future work - - compiles zenoh-c with debug logs active +### Packaging - - ```mvn -Prelease clean install``` +We intend to publish this code on Maven in the short term in order to ease the installation, but for the moment, until we +add some extra functionalities and test this library a bit further, we will hold the publication. - - compiles zenoh-c in release mode (without logs) - - cross-compiles zenoh-c on all supported platforms (incl. MacOS if this is your current host) using [dockross](https://github.com/dockcross/dockcross). - - generates the Javadoc - - generate a ZIP file for release in assembly/target +### Potential API changes +When using this library, keep in mind changes may occur, especially since this is the first version of the library. We have, however, +aimed to make the design as stable as possible from the very beginning, so changes on the code probably won't be substantial. -## Examples -See [examples/zenoh/README.md](examples/zenoh/) diff --git a/android-robot.png b/android-robot.png new file mode 100644 index 00000000..162313da Binary files /dev/null and b/android-robot.png differ diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..6944df56 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,42 @@ +// +// 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, +// + + +buildscript { + repositories { + google() + } + dependencies { + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0") + classpath("org.mozilla.rust-android-gradle:plugin:0.9.3") + classpath("com.android.tools.build:gradle:7.4.2") + } +} + +plugins { + id("com.android.library") version "7.4.2" apply false + id("org.jetbrains.kotlin.android") version "1.9.10" apply false + id("org.jetbrains.kotlin.multiplatform") version "1.9.0" apply false + id("org.mozilla.rust-android-gradle.rust-android") version "0.9.3" apply false + id("org.jetbrains.dokka") version "1.8.20" apply false + id("com.adarshr.test-logger") version "3.2.0" apply false + kotlin("plugin.serialization") version "1.9.0" apply false +} + +subprojects { + repositories { + google() + mavenCentral() + } +} diff --git a/docs/.gitignore b/docs/.gitignore deleted file mode 100644 index 5f796e0c..00000000 --- a/docs/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -# directories generated by Sphinx -_build -io -packages.rst diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index cf041cef..00000000 --- a/docs/conf.py +++ /dev/null @@ -1,42 +0,0 @@ -# -# Copyright (c) 2017, 2020 ADLINK Technology Inc. -# -# 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: -# ADLINK zenoh team, -# - -# Configuration file for the Sphinx documentation builder. - -# -- Project information ----------------------------------------------------- -project = 'zenoh-java' -copyright = '2017, 2020 ADLINK Technology Inc' -author = 'ADLINK zenoh team' -release = '0.4.2-M1' - -# -- General configuration --------------------------------------------------- -master_doc = 'index' -extensions = ['javasphinx'] -language = 'java' - -# -- Options for HTML output ------------------------------------------------- -html_theme = 'sphinx_rtd_theme' - - -def run_javasphinx_apidoc(_): - from javasphinx import apidoc - apidoc.main([ - "javasphinx-apidoc", - "-f", - "-t", "API Refrence", - "-o", ".", - "../zenoh/src/main/java/"]) - -def setup(app): - app.connect('builder-inited', run_javasphinx_apidoc) diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index d02771a2..00000000 --- a/docs/index.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. -.. Copyright (c) 2017, 2020 ADLINK Technology Inc. -.. -.. 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: -.. ADLINK zenoh team, -.. - -========== -zenoh-java -========== - -The *zenoh-java* library provides a Java `zenoh client API `_ for zenoh. - -An introduction to zenoh and its concepts is available on `zenoh.io `_. - -.. warning:: - zenoh has been subjet to a complete rewrite with major protocol updates between - versions 0.4.2 and 0.5.0. The Java API does not yet integrate those changes and is - only compatible with version 0.4.2 of the zenoh daemon and the underlying zenoh-c - stack. - -Note that this library also provides a low-level API (`zenoh-net `_) -that gives access to the zenoh protocol primitives and allow some -advanced use cases where a fine tuning of the protocol is required. - -.. toctree:: - :maxdepth: 1 - - zenoh API - zenoh-net API - diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 94fc71a1..00000000 --- a/docs/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -javasphinx \ No newline at end of file diff --git a/docs/zenoh-api.rst b/docs/zenoh-api.rst deleted file mode 100644 index c939126c..00000000 --- a/docs/zenoh-api.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. -.. Copyright (c) 2017, 2020 ADLINK Technology Inc. -.. -.. 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: -.. ADLINK zenoh team, -.. - -=================== -Zenoh API Reference -=================== - -.. toctree:: - :maxdepth: 2 - - org/eclipse/zenoh/package-index - org/eclipse/zenoh/core/package-index diff --git a/docs/zenoh-net-api.rst b/docs/zenoh-net-api.rst deleted file mode 100644 index 11f598bb..00000000 --- a/docs/zenoh-net-api.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. -.. Copyright (c) 2017, 2020 ADLINK Technology Inc. -.. -.. 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: -.. ADLINK zenoh team, -.. - -======================= -Zenoh-net API Reference -======================= - -.. toctree:: - :maxdepth: 2 - - org/eclipse/zenoh/net/package-index - org/eclipse/zenoh/core/package-index diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..a9e526bb --- /dev/null +++ b/examples/README.md @@ -0,0 +1,116 @@ +# Examples + +---- + +## Start instructions + + +```bash +./gradle +``` + +:warning: Passing arguments to these examples has not been enabled yet for this first version. Altering the Zenoh +configuration for these examples must be done programmatically. :warning: + +The examples are available for both Java and Kotlin versions. To chose one over the other, you need to specify the proper task. For instance, +to run the ZPub example with Kotlin run: + +```bash +./gradle examples:kotlin:ZPub +``` + +while to run the same example with the Java version, run: + +```bash +./gradle examples:java:ZPub +``` + + +The Java examples use the Java compatible Zenoh-Kotlin library, +so there are some differences inherent to the language. + +---- + +## Examples description + +### ZPub + +Declares a resource with a path and a publisher on this resource. Then puts a value using the numerical resource id. +The path/value will be received by all matching subscribers, for instance the [ZSub](#zsub) example. + +Usage: + +```bash +./gradle examples:kotlin:ZPub +``` + +### ZSub +Creates a subscriber with a key expression. +The subscriber will be notified of each put made on any key expression matching +the subscriber's key expression, and will print this notification. + +Usage: + +```bash +./gradle examples:kotlin:ZSub +``` + +### ZGet + +Sends a query message for a selector. +The queryables with a matching path or selector (for instance [ZQueryable](#zqueryable)) +will receive this query and reply with paths/values that will be received by the query callback. + +```bash +./gradle examples:kotlin:ZGet +``` + +### ZPut + +Puts a path/value into Zenoh. +The path/value will be received by all matching subscribers, for instance the [ZSub](#zsub). + +Usage: + +```bash +./gradle examples:kotlin:ZPut +``` + +### ZDelete +Performs a Delete operation into a path/value into Zenoh. + +Usage: + +```bash +./gradle examples:kotlin:ZDelete +``` + +### ZQueryable + +Creates a queryable function with a key expression. +This queryable function will be triggered by each call to a get operation on zenoh +with a selector that matches the key expression, and will return a value to the querier. + +Usage: + +```bash +./gradle examples:kotlin:ZQueryable +``` + +### ZPubThr & ZSubThr + +Pub/Sub throughput test. +This example allows to perform throughput measurements between a publisher performing +put operations and a subscriber receiving notifications of those puts. + +Subscriber usage: + +```bash +./gradle examples:kotlin:ZSubThr +``` + +Publisher usage: + +```bash +./gradle examples:kotlin:ZPubThr +``` diff --git a/examples/build.gradle.kts b/examples/build.gradle.kts new file mode 100644 index 00000000..18c92fee --- /dev/null +++ b/examples/build.gradle.kts @@ -0,0 +1,65 @@ +// +// 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, +// + +group = "io.zenoh" +version = "0.11.0-dev" + +plugins { + kotlin("jvm") +} + +kotlin { + jvmToolchain(11) +} + +dependencies { + implementation(project(":zenoh-java")) + implementation("commons-net:commons-net:3.9.0") +} + +tasks { + val examples = listOf( + "ZDelete", + "ZGet", + "ZPub", + "ZPubThr", + "ZPut", + "ZQueryable", + "ZSub", + "ZSubThr" + ) + + examples.forEach { example -> + register(example, JavaExec::class) { + dependsOn("CompileZenohJNI") + description = "Run the $example example" + mainClass.set("io.zenoh.${example}") + classpath(sourceSets["main"].runtimeClasspath) + val zenohPaths = "../zenoh-jni/target/release" + val defaultJvmArgs = arrayListOf("-Djava.library.path=$zenohPaths") + val loggerLvl = project.findProperty("zenoh.logger")?.toString() + if (loggerLvl != null) { + jvmArgs(defaultJvmArgs + "-Dzenoh.logger=$loggerLvl") + } else { + jvmArgs(defaultJvmArgs) + } + } + } +} + +tasks.register("CompileZenohJNI") { + project.exec { + commandLine("cargo", "build", "--release", "--manifest-path", "../zenoh-jni/Cargo.toml") + } +} diff --git a/examples/pom.xml b/examples/pom.xml deleted file mode 100644 index 7c3cc6b8..00000000 --- a/examples/pom.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - 4.0.0 - - - org.eclipse.zenoh - zenoh-java - 0.4.2-M1 - - - examples-parent-pom - pom - - Zenoh examples pom - - - zenoh - zenoh-net - - - diff --git a/examples/src/main/java/io/zenoh/ZDelete.java b/examples/src/main/java/io/zenoh/ZDelete.java new file mode 100644 index 00000000..7f7e726c --- /dev/null +++ b/examples/src/main/java/io/zenoh/ZDelete.java @@ -0,0 +1,30 @@ +// +// 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.exceptions.ZenohException; +import io.zenoh.keyexpr.KeyExpr; + +public class ZDelete { + public static void main(String[] args) throws ZenohException { + System.out.println("Opening session..."); + try (Session session = Session.open()) { + try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/zenoh-java-put")) { + System.out.println("Deleting resources matching '" + keyExpr + "'..."); + session.delete(keyExpr).res(); + } + } + } +} diff --git a/examples/src/main/java/io/zenoh/ZGet.java b/examples/src/main/java/io/zenoh/ZGet.java new file mode 100644 index 00000000..a8dbb262 --- /dev/null +++ b/examples/src/main/java/io/zenoh/ZGet.java @@ -0,0 +1,50 @@ +// +// 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.exceptions.ZenohException; +import io.zenoh.query.Reply; +import io.zenoh.selector.Selector; + +import java.util.Optional; +import java.util.concurrent.BlockingQueue; + +public class ZGet { + + public static void main(String[] args) throws ZenohException, InterruptedException { + System.out.println("Opening session..."); + try (Session session = Session.open()) { + try (Selector selector = Selector.tryFrom("demo/example/**")) { + System.out.println("Performing Get on '" + selector + "'..."); + BlockingQueue> receiver = session.get(selector).res(); + assert receiver != null; + while (true) { + Optional wrapper = receiver.take(); + if (wrapper.isEmpty()) { + break; + } + Reply reply = wrapper.get(); + if (reply instanceof Reply.Success) { + Reply.Success successReply = (Reply.Success) reply; + System.out.println("Received ('" + successReply.getSample().getKeyExpr() + "': '" + successReply.getSample().getValue() + "')"); + } else { + Reply.Error errorReply = (Reply.Error) reply; + System.out.println("Received (ERROR: '" + errorReply.getError() + "')"); + } + } + } + } + } +} diff --git a/examples/src/main/java/io/zenoh/ZPub.java b/examples/src/main/java/io/zenoh/ZPub.java new file mode 100644 index 00000000..cf798eb8 --- /dev/null +++ b/examples/src/main/java/io/zenoh/ZPub.java @@ -0,0 +1,40 @@ +// +// 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.exceptions.ZenohException; +import io.zenoh.keyexpr.KeyExpr; +import io.zenoh.publication.Publisher; + +public class ZPub { + public static void main(String[] args) throws ZenohException, InterruptedException { + System.out.println("Opening session..."); + try (Session session = Session.open()) { + try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/zenoh-java-pub")) { + System.out.println("Declaring publisher on '" + keyExpr + "'..."); + try (Publisher publisher = session.declarePublisher(keyExpr).res()) { + String payload = "Pub from Java!"; + int idx = 0; + while (true) { + Thread.sleep(1000); + System.out.println("Putting Data ('" + keyExpr + "': '[" + String.format("%4s", idx) + "] " + payload + "')..."); + publisher.put(payload).res(); + idx++; + } + } + } + } + } +} diff --git a/examples/src/main/java/io/zenoh/ZPubThr.java b/examples/src/main/java/io/zenoh/ZPubThr.java new file mode 100644 index 00000000..1ec30567 --- /dev/null +++ b/examples/src/main/java/io/zenoh/ZPubThr.java @@ -0,0 +1,45 @@ +// +// 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.exceptions.ZenohException; +import io.zenoh.keyexpr.KeyExpr; +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; + +public class ZPubThr { + + public static void main(String[] args) throws ZenohException { + int size = 8; + byte[] data = new byte[size]; + for (int i = 0; i < size; i++) { + data[i] = (byte) (i % 10); + } + Value value = new Value(data, new Encoding(KnownEncoding.EMPTY)); + try (Session session = Session.open()) { + try (KeyExpr keyExpr = KeyExpr.tryFrom("test/thr")) { + try (Publisher publisher = session.declarePublisher(keyExpr).congestionControl(CongestionControl.BLOCK).res()) { + System.out.println("Publisher declared on test/thr."); + while (true) { + publisher.put(value).res(); + } + } + } + } + } +} diff --git a/examples/src/main/java/io/zenoh/ZPut.java b/examples/src/main/java/io/zenoh/ZPut.java new file mode 100644 index 00000000..ba1196b4 --- /dev/null +++ b/examples/src/main/java/io/zenoh/ZPut.java @@ -0,0 +1,38 @@ +// +// 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.exceptions.ZenohException; +import io.zenoh.keyexpr.KeyExpr; +import io.zenoh.prelude.SampleKind; +import io.zenoh.publication.CongestionControl; +import io.zenoh.publication.Priority; + +public class ZPut { + public static void main(String[] args) throws ZenohException { + System.out.println("Opening session..."); + try (Session session = Session.open()) { + try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/zenoh-java-put")) { + String value = "Put from Java!"; + 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 new file mode 100644 index 00000000..1dd67503 --- /dev/null +++ b/examples/src/main/java/io/zenoh/ZQueryable.java @@ -0,0 +1,58 @@ +// +// 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.exceptions.ZenohException; +import io.zenoh.keyexpr.KeyExpr; +import io.zenoh.prelude.SampleKind; +import io.zenoh.queryable.Query; +import io.zenoh.queryable.Queryable; +import org.apache.commons.net.ntp.TimeStamp; + +import java.util.Optional; +import java.util.concurrent.BlockingQueue; + +public class ZQueryable { + + public static void main(String[] args) throws ZenohException, InterruptedException { + try (Session session = Session.open()) { + try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/zenoh-java-queryable")) { + System.out.println("Declaring Queryable"); + try (Queryable>> queryable = session.declareQueryable(keyExpr).res()) { + BlockingQueue> receiver = queryable.getReceiver(); + assert receiver != null; + handleRequests(receiver, keyExpr); + } + } + } + } + + private static void handleRequests(BlockingQueue> receiver, KeyExpr keyExpr) throws InterruptedException { + while (true) { + Optional wrapper = receiver.take(); + if (wrapper.isEmpty()) { + break; + } + Query query = wrapper.get(); + 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(); + } catch (Exception e) { + System.out.println(">> [Queryable] Error sending reply: " + e); + } + } + } +} diff --git a/examples/src/main/java/io/zenoh/ZSub.java b/examples/src/main/java/io/zenoh/ZSub.java new file mode 100644 index 00000000..bcf0710d --- /dev/null +++ b/examples/src/main/java/io/zenoh/ZSub.java @@ -0,0 +1,47 @@ +// +// 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.exceptions.ZenohException; +import io.zenoh.keyexpr.KeyExpr; +import io.zenoh.sample.Sample; +import io.zenoh.subscriber.Subscriber; + +import java.util.Optional; +import java.util.concurrent.BlockingQueue; + +public class ZSub { + + public static void main(String[] args) throws ZenohException, InterruptedException { + System.out.println("Opening session..."); + try (Session session = Session.open()) { + try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/**")) { + System.out.println("Declaring Subscriber on '" + keyExpr + "'..."); + try (Subscriber>> subscriber = session.declareSubscriber(keyExpr).res()) { + BlockingQueue> receiver = subscriber.getReceiver(); + assert receiver != null; + while (true) { + Optional wrapper = receiver.take(); + if (wrapper.isEmpty()) { + break; + } + Sample sample = wrapper.get(); + System.out.println(">> [Subscriber] Received " + sample.getKind() + " ('" + sample.getKeyExpr() + "': '" + sample.getValue() + "')"); + } + } + } + } + } +} diff --git a/examples/src/main/java/io/zenoh/ZSubThr.java b/examples/src/main/java/io/zenoh/ZSubThr.java new file mode 100644 index 00000000..d226fb68 --- /dev/null +++ b/examples/src/main/java/io/zenoh/ZSubThr.java @@ -0,0 +1,77 @@ +// +// 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.exceptions.ZenohException; +import io.zenoh.keyexpr.KeyExpr; +import io.zenoh.subscriber.Subscriber; +import kotlin.Unit; + +import java.util.Scanner; + +public class ZSubThr { + + private static final long NANOS_TO_SEC = 1_000_000_000L; + private static final long n = 50000L; + private static int batchCount = 0; + private static int count = 0; + private static long startTimestampNs = 0; + private static long globalStartTimestampNs = 0; + + public static void listener() { + if (count == 0) { + startTimestampNs = System.nanoTime(); + if (globalStartTimestampNs == 0L) { + globalStartTimestampNs = startTimestampNs; + } + count++; + return; + } + if (count < n) { + count++; + return; + } + long stop = System.nanoTime(); + double msgs = (double) (n * NANOS_TO_SEC) / (stop - startTimestampNs); + System.out.println(msgs + " msgs/sec"); + batchCount++; + count = 0; + } + + public static void report() { + long end = System.nanoTime(); + long total = batchCount * n + count; + double msgs = (double) (end - globalStartTimestampNs) / NANOS_TO_SEC; + double avg = (double) (total * NANOS_TO_SEC) / (end - globalStartTimestampNs); + System.out.println("Received " + total + " messages in " + msgs + + ": averaged " + avg + " msgs/sec"); + } + + public static void main(String[] args) throws ZenohException { + System.out.println("Opening Session"); + try (Session session = Session.open()) { + try (KeyExpr keyExpr = KeyExpr.tryFrom("test/thr")) { + try (Subscriber subscriber = session.declareSubscriber(keyExpr).with(sample -> listener()).res()) { + Scanner scanner = new Scanner(System.in); + while (!scanner.nextLine().equals("q")) { + // Do nothing + } + scanner.close(); + } + } + } + report(); + } +} diff --git a/examples/zenoh-net/README.md b/examples/zenoh-net/README.md deleted file mode 100644 index b99a3221..00000000 --- a/examples/zenoh-net/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Zenoh-net Java examples - -## Build instructions - - ```bash - mvn package - ``` - -## Start instructions - - The jar file produces by the Maven build is standalone (i.e. it contains all the resuired dependencies). - You can run any example Java class with such command: - - ```bash - java -cp target/zenoh-net-examples-.jar - ``` diff --git a/examples/zenoh-net/pom.xml b/examples/zenoh-net/pom.xml deleted file mode 100644 index 23e1224b..00000000 --- a/examples/zenoh-net/pom.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - 4.0.0 - - org.eclipse.zenoh - zenoh-net-examples - 0.4.2-M1 - jar - - Zenoh-net examples - Zenoh examples using only the zenoh-net API - - - UTF-8 - 1.8 - 1.8 - true - - ${project.version} - - - - - org.eclipse.zenoh - zenoh - ${zenoh.version} - - - - - ch.qos.logback - logback-core - 1.2.3 - - - ch.qos.logback - logback-classic - 1.2.3 - - - - - info.picocli - picocli - 4.2.0 - - - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.1.0 - - - package - - shade - - - false - ${project.build.directory}/dependency-reduced-pom.xml - - - - - - - - diff --git a/examples/zenoh-net/src/main/java/ZNEval.java b/examples/zenoh-net/src/main/java/ZNEval.java deleted file mode 100644 index 45c5e996..00000000 --- a/examples/zenoh-net/src/main/java/ZNEval.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import org.eclipse.zenoh.net.*; - -import java.io.InputStreamReader; -import java.nio.ByteBuffer; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNEval implements QueryHandler, Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-p", "--path"}, description = "the path representing the URI.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/java/eval"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - // zenoh Eval's callback - @Override - public void handleQuery(String rname, String predicate, RepliesSender repliesSender) { - System.out.printf(">> [Query handler] Handling '%s?%s'\n", rname, predicate); - - ByteBuffer data = ByteBuffer.wrap("Eval from Java!".getBytes()); - Resource[] replies = { new Resource(path, data, 0, 0) }; - - repliesSender.sendReplies(replies); - } - - @Override - public void run() { - try { - System.out.println("Openning session..."); - Session s = Session.open(locator); - - System.out.println("Declaring Eval on '" + path + "'..."); - Eval e = s.declareEval(path, new ZNEval()); - - InputStreamReader stdin = new InputStreamReader(System.in); - while ((char) stdin.read() != 'q') - ; - - e.undeclare(); - s.close(); - - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNEval()).execute(args); - System.exit(exitCode); - } - -} diff --git a/examples/zenoh-net/src/main/java/ZNInfo.java b/examples/zenoh-net/src/main/java/ZNInfo.java deleted file mode 100644 index 73bc6709..00000000 --- a/examples/zenoh-net/src/main/java/ZNInfo.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import java.util.HashMap; -import java.util.Map; - -import org.eclipse.zenoh.net.*; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNInfo implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); - - public static String hexdump(byte[] bytes) { - StringBuffer sb = new StringBuffer(); - for (byte b : bytes) { - sb.append(HEX_DIGITS[(b & 0xF0) >> 4]).append(HEX_DIGITS[b & 0x0F]); - } - return sb.toString(); - } - - @Override - public void run() { - try { - System.out.println("Openning session..."); - Map properties = new HashMap(2); - properties.put(ZNet.USER_KEY, "user".getBytes()); - properties.put(ZNet.PASSWD_KEY, "password".getBytes()); - Session s = Session.open(locator, properties); - - Map info = s.info(); - System.out.println("LOCATOR : " + new String(info.get(ZNet.INFO_PEER_KEY))); - System.out.println("PID : " + hexdump(info.get(ZNet.INFO_PID_KEY))); - System.out.println("PEER PID : " + hexdump(info.get(ZNet.INFO_PEER_PID_KEY))); - - s.close(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNInfo()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh-net/src/main/java/ZNPubThr.java b/examples/zenoh-net/src/main/java/ZNPubThr.java deleted file mode 100644 index 87fd8ecd..00000000 --- a/examples/zenoh-net/src/main/java/ZNPubThr.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import org.eclipse.zenoh.net.*; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNPubThr implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-s", "--size"}, - description = "the size in bytes of the payload used for the throughput test. [default: ${DEFAULT-VALUE}]") - private int size = 256; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-p", "--path"}, - description = "the resource used to write throughput data.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/throughput/data"; - - @Option(names = {"-b", "--buffer"}, - description = "The type of ByteBuffer to use. Valid values: ${COMPLETION-CANDIDATES}. [default: ${DEFAULT-VALUE}]") - private BufferKind bufferKind = BufferKind.DIRECT; - - private enum BufferKind { - DIRECT, - NON_DIRECT, - WRAPPED, - } - - @Override - public void run() { - java.nio.ByteBuffer data = null; - switch (bufferKind) { - case DIRECT: - data = java.nio.ByteBuffer.allocateDirect(size); - System.out.println("Running throughput test for payload of " + size + " bytes from a direct ByteBuffer"); - break; - case NON_DIRECT: - data = java.nio.ByteBuffer.allocate(size); - System.out.println("Running throughput test for payload of " + size + " bytes from a non-direct ByteBuffer"); - break; - case WRAPPED: - // allocate more than len, to wrap with an offset and test the impact - byte[] array = new byte[size + 1024]; - data = java.nio.ByteBuffer.wrap(array, 100, size); - System.out.println("Running throughput test for payload of " + size + " bytes from a wrapped ByteBuffer"); - break; - } - - int posInit = data.position(); - for (int i = 0; i < size; ++i) { - data.put((byte) (i % 10)); - } - data.flip(); - data.position(posInit); - - try { - Session s = Session.open(locator); - Publisher pub = s.declarePublisher(path); - - while (true) { - pub.streamData(data); - } - - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNPubThr()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh-net/src/main/java/ZNPull.java b/examples/zenoh-net/src/main/java/ZNPull.java deleted file mode 100644 index b777d1a2..00000000 --- a/examples/zenoh-net/src/main/java/ZNPull.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import org.eclipse.zenoh.net.*; - -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNPull implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-s", "--selector"}, - description = "The selector to be used for issuing the pull subscription. [default: ${DEFAULT-VALUE}]") - private String selector = "/zenoh/examples/**"; - - private static class Listener implements DataHandler { - @Override - public void handleData(String rname, ByteBuffer data, DataInfo info) { - byte[] buf = new byte[data.remaining()]; - data.get(buf); - try { - String str = new String(buf, "UTF-8"); - System.out.printf(">> [Subscription listener] Received ('%s': '%s')\n", rname, str); - } catch (UnsupportedEncodingException e) { - System.out.printf(">> [Subscription listener] Received ('%s': '%s')\n", rname, data.toString()); - } - } - } - - @Override - public void run() { - try { - System.out.println("Openning session..."); - Session s = Session.open(locator); - - System.out.println("Declaring Subscriber on '" + selector + "'..."); - Subscriber sub = s.declareSubscriber(selector, SubMode.pull(), new Listener()); - - System.out.println("Press to pull data..."); - InputStreamReader stdin = new InputStreamReader(System.in); - while ((char) stdin.read() != 'q') { - sub.pull(); - } - - sub.undeclare(); - s.close(); - - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNPull()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh-net/src/main/java/ZNQuery.java b/examples/zenoh-net/src/main/java/ZNQuery.java deleted file mode 100644 index a38312ba..00000000 --- a/examples/zenoh-net/src/main/java/ZNQuery.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import org.eclipse.zenoh.net.*; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNQuery implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-s", "--selector"}, - description = "The selector to be used for issuing the pull query.\n [default: ${DEFAULT-VALUE}]") - private String selector = "/zenoh/examples/**"; - - private static class Handler implements ReplyHandler { - - public void handleReply(ReplyValue reply) { - switch (reply.getKind()) { - case ZN_STORAGE_DATA: - case ZN_EVAL_DATA: - java.nio.ByteBuffer data = reply.getData(); - try { - byte[] buf = new byte[data.remaining()]; - data.get(buf); - String s = new String(buf, "UTF-8"); - if (reply.getKind() == ReplyValue.Kind.ZN_STORAGE_DATA) { - System.out.printf(">> [Reply handler] received -Storage Data- ('%s': '%s')\n", reply.getRname(), - s); - } else { - System.out.printf(">> [Reply handler] received -Eval Data- ('%s': '%s')\n", reply.getRname(), - s); - } - } catch (java.io.UnsupportedEncodingException e) { - System.out.println(">> [Reply handler] error decoding data: " + e); - } - break; - case ZN_STORAGE_FINAL: - System.out.println(">> [Reply handler] received -Storage Final-"); - break; - case ZN_EVAL_FINAL: - System.out.println(">> [Reply handler] received -Eval Final-"); - break; - case ZN_REPLY_FINAL: - System.out.println(">> [Reply handler] received -Reply Final-"); - break; - } - } - } - - - @Override - public void run() { - try { - System.out.println("Openning session..."); - Session s = Session.open(locator); - - System.out.println("Send query '" + selector + "'..."); - s.query(selector, "", new Handler(), QueryDest.all(), QueryDest.all()); - - Thread.sleep(1000); - - s.close(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNQuery()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh-net/src/main/java/ZNStorage.java b/examples/zenoh-net/src/main/java/ZNStorage.java deleted file mode 100644 index f3c03233..00000000 --- a/examples/zenoh-net/src/main/java/ZNStorage.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -import org.eclipse.zenoh.net.*; - -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; -import java.util.Map; -import java.util.Vector; -import java.util.HashMap; -import java.util.List; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNStorage implements StorageHandler, Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-s", "--selector"}, - description = "the selector associated with this storage.\n [default: ${DEFAULT-VALUE}]") - private String selector = "/zenoh/examples/**"; - - private Map stored = new HashMap(); - - public void handleData(String rname, ByteBuffer data, DataInfo info) { - try { - byte[] buf = new byte[data.remaining()]; - data.get(buf); - String str = new String(buf, "UTF-8"); - System.out.printf(">> [Subscription listener] Received ('%s': '%s')\n", rname, str); - } catch (UnsupportedEncodingException e) { - System.out.printf(">> [Subscription listener] Received ('%s': '%s')\n", rname, data.toString()); - } - data.rewind(); - this.stored.put(rname, data); - } - - public void handleQuery(String rname, String predicate, RepliesSender repliesSender) { - System.out.printf(">> [Query handler ] Handling '%s?%s'\n", rname, predicate); - - List replies = new Vector(); - for (Map.Entry entry : stored.entrySet()) { - if (Rname.intersect(rname, entry.getKey())) { - replies.add(new Resource(entry.getKey(), entry.getValue(), 0, 0)); - } - } - repliesSender.sendReplies(replies.toArray(new Resource[replies.size()])); - } - - - @Override - public void run() { - try { - System.out.println("Openning session..."); - Session s = Session.open(locator); - - System.out.println("Declaring Storage on '" + selector + "'..."); - Storage sto = s.declareStorage(selector, new ZNStorage()); - - InputStreamReader stdin = new InputStreamReader(System.in); - while ((char) stdin.read() != 'q') - ; - - sto.undeclare(); - s.close(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNStorage()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh-net/src/main/java/ZNStream.java b/examples/zenoh-net/src/main/java/ZNStream.java deleted file mode 100644 index 638f3cc0..00000000 --- a/examples/zenoh-net/src/main/java/ZNStream.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import org.eclipse.zenoh.net.*; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNStream implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-p", "--path"}, - description = "the path representing the URI.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/java/stream/hello"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-m", "--msg"}, - description = "The quote associated with the welcoming resource.\n [default: ${DEFAULT-VALUE}]") - private String msg = "Zenitude streamed from zenoh-net-java!"; - - @Override - public void run() { - try { - System.out.println("Openning session..."); - Session s = Session.open(locator); - - System.out.println("Declaring Publisher on '" + path + "'..."); - Publisher pub = s.declarePublisher(path); - - System.out.println("Streaming Data..."); - java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocateDirect(256); - for (int idx = 0; idx < 100; ++idx) { - Thread.sleep(1000); - String str = String.format("[%4d] %s", idx, msg); - buf.put(str.getBytes("UTF-8")); - System.out.printf("Streaming Data ('%s': '%s')...\n", path, str); - pub.streamData(buf); - buf.rewind(); - } - - pub.undeclare(); - s.close(); - - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNStream()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh-net/src/main/java/ZNSub.java b/examples/zenoh-net/src/main/java/ZNSub.java deleted file mode 100644 index 1c8da36c..00000000 --- a/examples/zenoh-net/src/main/java/ZNSub.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import org.eclipse.zenoh.net.*; - -import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNSub implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-s", "--selector"}, - description = "The selector specifying the subscription.\n [default: ${DEFAULT-VALUE}]") - private String selector = "/zenoh/examples/**"; - - private static class Listener implements DataHandler { - public void handleData(String rname, ByteBuffer data, DataInfo info) { - try { - byte[] buf = new byte[data.remaining()]; - data.get(buf); - String str = new String(buf, "UTF-8"); - System.out.printf(">> [Subscription listener] Received ('%s': '%s')\n", rname, str); - } catch (UnsupportedEncodingException e) { - System.out.printf(">> [Subscription listener] Received ('%s': '%s')\n", rname, data.toString()); - } - } - } - - @Override - public void run() { - try { - System.out.println("Openning session..."); - Session s = Session.open(locator); - - System.out.println("Declaring Subscriber on '" + selector + "'..."); - Subscriber sub = s.declareSubscriber(selector, SubMode.push(), new Listener()); - - InputStreamReader stdin = new InputStreamReader(System.in); - while ((char) stdin.read() != 'q') - ; - - sub.undeclare(); - s.close(); - - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNSub()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh-net/src/main/java/ZNSubThr.java b/examples/zenoh-net/src/main/java/ZNSubThr.java deleted file mode 100644 index 255c2c37..00000000 --- a/examples/zenoh-net/src/main/java/ZNSubThr.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import org.eclipse.zenoh.net.*; - -import java.nio.ByteBuffer; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNSubThr implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-p", "--path"}, - description = "The subscriber path.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/throughput/data"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - private static final long N = 100000; - - private static long count = 0; - private static long start; - private static long stop; - - public static void printStats(long start, long stop) { - float delta = stop - start; - float thpt = N / (delta / 1000); - System.out.format("%f msgs/sec%n", thpt); - } - - private static class Listener implements DataHandler { - public void handleData(String rname, ByteBuffer data, DataInfo info) { - if (count == 0) { - start = System.currentTimeMillis(); - count++; - } else if (count < N) { - count++; - } else { - stop = System.currentTimeMillis(); - printStats(start, stop); - System.gc(); - count = 0; - } - } - } - - @Override - public void run() { - try { - Session s = Session.open(locator); - Subscriber sub = s.declareSubscriber(path, SubMode.push(), new Listener()); - - Thread.sleep(60000); - - sub.undeclare(); - s.close(); - - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNSubThr()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh-net/src/main/java/ZNWrite.java b/examples/zenoh-net/src/main/java/ZNWrite.java deleted file mode 100644 index 1df9fac6..00000000 --- a/examples/zenoh-net/src/main/java/ZNWrite.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import org.eclipse.zenoh.net.*; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZNWrite implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-p", "--path"}, - description = "the path representing the URI.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/java/write/hello"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-m", "--msg"}, - description = "The quote associated with the welcoming resource.\n [default: ${DEFAULT-VALUE}]") - private String msg = "Zenitude written from zenoh-net-java!"; - - @Override - public void run() { - try { - System.out.println("Openning session..."); - Session s = Session.open(locator); - - System.out.printf("Writing Data ('%s': '%s')...\n", path, msg); - java.nio.ByteBuffer buf = java.nio.ByteBuffer.wrap(msg.getBytes("UTF-8")); - s.writeData(path, buf); - - s.close(); - - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZNWrite()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh-net/src/main/resources/logback.xml b/examples/zenoh-net/src/main/resources/logback.xml deleted file mode 100644 index b7ae2de2..00000000 --- a/examples/zenoh-net/src/main/resources/logback.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - true - - - - - - - - - - %highlight(%d{HH:mm:ss.SSS} %.-1level [%-18.18thread] %-20.20logger : %msg%n) - - - - - - - - - - - - diff --git a/examples/zenoh/README.md b/examples/zenoh/README.md deleted file mode 100644 index 1cc6d199..00000000 --- a/examples/zenoh/README.md +++ /dev/null @@ -1,127 +0,0 @@ -# Zenoh Java examples - -## Build instructions - - ```bash - mvn package - ``` - -## Start instructions - - The jar file produces by the Maven build is standalone (i.e. it contains all the required dependencies). - You can run any example Java class with such command: - - ```bash - java -cp target/zenoh-examples-.jar - ``` - - Each example accepts the -h or --help option that provides a description of its arguments and their default values. - -## Examples description - -### ZAddStorage - - Add a storage in the Zenoh router it's connected to. - - Usage: - ```bash - java -cp target/zenoh-examples-.jar ZAddStorage [--selector SELECTOR] [--id ID] [--locator LOCATOR] - ``` - - Note that his example doesn't specify the Backend that Zenoh has to use for storage creation. - Therefore, Zenoh will automatically select the memory backend, meaning the storage will be in memory - (i.e. not persistent). - -### ZPut - - Put a key/value into Zenoh. - The key/value will be stored by all the storages with a selector that matches the key. - It will also be received by all the matching subscribers (see [ZSub](#ZSub) below). - Note that if no storage and no subscriber are matching the key, the key/value will be dropped. - Therefore, you probably should run [ZAddStorage](#ZAddStorage) and/or [ZSub](#ZSub) before ZPut. - - Usage: - ```bash - java -cp target/zenoh-examples-.jar ZPut [--path PATH] [--locator LOCATOR] [--msg MSG] - ``` - -### ZPutFloat - - Put a key/value into Zenoh where the value is a float. - The key/value will be stored by all the storages with a selector that matches the key. - It will also be received by all the matching subscribers (see [ZSub](#ZSub) below). - Note that if no storage and no subscriber are matching the key, the key/value will be dropped. - Therefore, you probably should run [ZAddStorage](#ZAddStorage) and/or [ZSub](#ZSub) before ZPutFloat. - - Usage: - ```bash - java -cp target/zenoh-examples-.jar ZPutFloat [-l=] [-p=] - ``` - -### ZGet - - Get a list of keys/values from Zenoh. - The values will be retrieved from the Storages containing paths that match the specified selector. - The Eval functions (see [ZEval](#ZEval) below) registered with a path matching the selector - will also be triggered. - - Usage: - ```bash - java -cp target/zenoh-examples-.jar ZGet [--selector SELECTOR] [--locator LOCATOR] - ``` - -### ZRemove - - Remove a key and its associated value from Zenoh. - Any storage that store the key/value will drop it. - The subscribers with a selector matching the key will also receive a notification of this removal. - - Usage: - ```bash - java -cp target/zenoh-examples-.jar ZRemove [--path PATH] [--locator LOCATOR] - ``` - -### ZSub - - Register a subscriber with a selector. - The subscriber will be notified of each put/remove made on any path matching the selector, - and will print this notification. - - Usage: - ```bash - java -cp target/zenoh-examples-.jar ZSub [--selector SELECTOR] [--locator LOCATOR] - ``` - -### ZEval - - Register an evaluation function with a path. - This evaluation function will be triggered by each call to a get operation on Zenoh - with a selector that matches the path. In this example, the function returns a string value. - See the code for more details. - - Usage: - ```bash - java -cp target/zenoh-examples-.jar ZEval [--path PATH] [--locator LOCATOR] - ``` - -### ZPutThr & ZSubThr - - Pub/Sub throughput test. - This example allows to perform throughput measurements between a pubisher performing - put operations and a subscriber receiving notifications of those put. - Note that you can run this example with or without any storage. - - Subscriber usage: - ```bash - java -cp target/zenoh-examples-.jar ZSubThr [--path PATH] [--locator LOCATOR] - ``` - - Publisher usage: - ```bash - java -cp target/zenoh-examples-.jar ZPutThr [--size SIZE] [--locator LOCATOR] [--path PATH] [-b=BUFFER_KIND] - ``` - - where the BUFFER_KIND option specifies the way to allocate the java.util.ByteBuffer payload that will be put into Zenoh. The possible values are: - - **DIRECT**: use a direct ByteBuffer. This is the default value. - - **NON_DIRECT**: use a non-direct ByteBuffer (created via ByteBuffer.allocate()) - - **WRAPPED**: use a wrapped ByteBuffer (created via ByteBuffer.wrap()) diff --git a/examples/zenoh/pom.xml b/examples/zenoh/pom.xml deleted file mode 100644 index 7227d573..00000000 --- a/examples/zenoh/pom.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - 4.0.0 - - org.eclipse.zenoh - zenoh-examples - 0.4.2-M1 - jar - - Zenoh examples - Zenoh examples - - - UTF-8 - 1.8 - 1.8 - true - - ${project.version} - - - - - org.eclipse.zenoh - zenoh - ${zenoh.version} - - - - - ch.qos.logback - logback-core - 1.2.3 - - - ch.qos.logback - logback-classic - 1.2.3 - - - - - info.picocli - picocli - 4.2.0 - - - - - - - - org.apache.maven.plugins - maven-shade-plugin - 3.1.0 - - - package - - shade - - - false - ${project.build.directory}/dependency-reduced-pom.xml - - - - - - - - diff --git a/examples/zenoh/src/main/java/ZAddStorage.java b/examples/zenoh/src/main/java/ZAddStorage.java deleted file mode 100644 index ed473cc9..00000000 --- a/examples/zenoh/src/main/java/ZAddStorage.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -import org.eclipse.zenoh.*; - -import java.util.Properties; -import picocli.CommandLine; -import picocli.CommandLine.Option; - -public class ZAddStorage implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-s", "--selector"}, - description = "the selector associated with this storage.\n [default: ${DEFAULT-VALUE}]") - private String selector = "/zenoh/examples/**"; - - @Option(names = {"-i", "--id"}, - description = "the storage identifier.\n [default: ${DEFAULT-VALUE}]") - private String storageId = "zenoh-examples-storage"; - - @Override - public void run() { - try { - System.out.println("Login to Zenoh (locator=" + locator + ")..."); - Zenoh z = Zenoh.login(locator); - - Admin admin = z.admin(); - - System.out.println("Add storage " + storageId + " with selector " + selector); - Properties p = new Properties(); - p.setProperty("selector", selector); - admin.addStorage(storageId, p); - - z.logout(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZAddStorage()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh/src/main/java/ZEval.java b/examples/zenoh/src/main/java/ZEval.java deleted file mode 100644 index 8aba43a7..00000000 --- a/examples/zenoh/src/main/java/ZEval.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -import org.eclipse.zenoh.*; - -import java.io.InputStreamReader; -import java.util.Collection; -import java.util.Properties; -import picocli.CommandLine; -import picocli.CommandLine.Option; - -public class ZEval implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-p", "--path"}, - description = "the path representing the URI.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/java/eval"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Override - public void run() { - try { - Path p = new Path(path); - - System.out.println("Login to Zenoh (locator=" + locator + ")..."); - Zenoh z = Zenoh.login(locator); - - System.out.println("Use Workspace on '/'"); - // Note that we use a Workspace with executor here, for our Eval.callback - // below to be called in a separate thread rather that in Zenoh I/O thread. - // Thus, the callback can perform some Zenoh operations (e.g.: get) - Workspace w = z.workspaceWithExecutor(); - - System.out.println("Register eval " + p); - w.registerEval(p, new Eval() { - public Value callback(Path p, Properties properties) { - // In this Eval function, we choosed to get the name to be returned in the - // StringValue in 3 possible ways, - // depending the properties specified in the selector. For example, with the - // following selectors: - // - "/zenoh/examples/java/eval" : no properties are set, a default value is - // used for the name - // - "/zenoh/examples/java/eval?(name=Bob)" : "Bob" is used for the name - // - "/zenoh/examples/java/eval?(name=/zenoh/examples/name)" : - // the Eval function does a GET on "/zenoh/examples/name" an uses the 1st result - // for the name - - System.out.printf(">> Processing eval for path %s with properties: %s\n", p, properties); - String name = properties.getProperty("name", "Zenoh Java!"); - - if (name.startsWith("/")) { - try { - System.out.printf(" >> Get name to use from Zenoh at path: %s\n", name); - Collection data = w.get(new Selector(name)); - if (!data.isEmpty()) { - name = data.iterator().next().getValue().toString(); - } - } catch (Throwable e) { - System.err.println("Failed to get value from path " + name); - e.printStackTrace(); - } - } - System.out.printf(" >> Returning string: \"Eval from %s\"\n", name); - return new StringValue("Eval from " + name); - } - }); - - System.out.println("Enter 'q' to quit...\n"); - InputStreamReader stdin = new InputStreamReader(System.in); - while ((char) stdin.read() != 'q') - ; - - w.unregisterEval(p); - z.logout(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZEval()).execute(args); - System.exit(exitCode); - } -} \ No newline at end of file diff --git a/examples/zenoh/src/main/java/ZGet.java b/examples/zenoh/src/main/java/ZGet.java deleted file mode 100644 index 64643d7e..00000000 --- a/examples/zenoh/src/main/java/ZGet.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -import org.eclipse.zenoh.*; -import picocli.CommandLine; -import picocli.CommandLine.Option; - -public class ZGet implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-s", "--selector"}, - description = "The selector to be used for issuing the query.\n [default: ${DEFAULT-VALUE}]") - private String selector = "/zenoh/examples/**"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Override - public void run() { - try { - Selector s = new Selector(selector); - - System.out.println("Login to Zenoh (locator=" + locator + ")..."); - Zenoh z = Zenoh.login(locator); - - Workspace w = z.workspace(); - - System.out.println("Get from " + s); - for (Data data : w.get(s)) { - System.out.println(" " + data.getPath() + " : " + data.getValue() + " - " + data.getValue().getEncoding()); - } - - z.logout(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZGet()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh/src/main/java/ZPut.java b/examples/zenoh/src/main/java/ZPut.java deleted file mode 100644 index 3e25be14..00000000 --- a/examples/zenoh/src/main/java/ZPut.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -import org.eclipse.zenoh.*; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -public class ZPut implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-p", "--path"}, - description = "the path representing the URI.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/java/put/hello"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-m", "--msg"}, - description = "The quote associated with the welcoming resource.\n [default: ${DEFAULT-VALUE}]") - private String msg = "Zenitude put from zenoh-java!"; - - @Override - public void run() { - try { - Path p = new Path(path); - Value v = new StringValue(msg); - - System.out.println("Login to Zenoh (locator=" + locator + ")..."); - Zenoh z = Zenoh.login(locator); - - Workspace w = z.workspace(); - - System.out.println("Put on " + p + " : " + v); - w.put(p, v); - - z.logout(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZPut()).execute(args); - System.exit(exitCode); - } -} \ No newline at end of file diff --git a/examples/zenoh/src/main/java/ZPutFloat.java b/examples/zenoh/src/main/java/ZPutFloat.java deleted file mode 100644 index 9dae00cf..00000000 --- a/examples/zenoh/src/main/java/ZPutFloat.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - - -import org.eclipse.zenoh.*; - -import java.io.BufferedReader; -import java.io.InputStreamReader; -import picocli.CommandLine; -import picocli.CommandLine.Option; - -public class ZPutFloat implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-p", "--path"}, - description = "the path representing the float resource.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/native/float"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Override - public void run() { - try { - Path p = new Path(path); - - Zenoh z = Zenoh.login(locator); - Workspace w = z.workspace(); - - BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); - String v = null; - while (! ".".equals(v)) { - System.out.print("Insert value ('.' to exit): "); - v = reader.readLine(); - try { - w.put(p, Float.parseFloat(v)); - } catch (NumberFormatException e) { - System.err.println("Invalid float!"); - } - } - - z.logout(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZPutFloat()).execute(args); - System.exit(exitCode); - } -} \ No newline at end of file diff --git a/examples/zenoh/src/main/java/ZPutThr.java b/examples/zenoh/src/main/java/ZPutThr.java deleted file mode 100644 index c1956f86..00000000 --- a/examples/zenoh/src/main/java/ZPutThr.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -import org.eclipse.zenoh.Path; -import org.eclipse.zenoh.RawValue; -import org.eclipse.zenoh.Value; -import org.eclipse.zenoh.Workspace; -import org.eclipse.zenoh.Zenoh; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZPutThr implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-s", "--size"}, - description = "the size in bytes of the payload used for the throughput test. [default: ${DEFAULT-VALUE}]") - private int size = 256; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Option(names = {"-p", "--path"}, - description = "the resource used to write throughput data.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/throughput/data"; - - @Option(names = {"-b", "--buffer"}, - description = "The type of ByteBuffer to use. Valid values: ${COMPLETION-CANDIDATES}. [default: ${DEFAULT-VALUE}]") - private BufferKind bufferKind = BufferKind.DIRECT; - - private enum BufferKind { - DIRECT, - NON_DIRECT, - WRAPPED, - } - - @Override - public void run() { - java.nio.ByteBuffer data = null; - switch (bufferKind) { - case DIRECT: - data = java.nio.ByteBuffer.allocateDirect(size); - System.out.println("Running throughput test for payload of " + size + " bytes from a direct ByteBuffer"); - break; - case NON_DIRECT: - data = java.nio.ByteBuffer.allocate(size); - System.out.println("Running throughput test for payload of " + size + " bytes from a non-direct ByteBuffer"); - break; - case WRAPPED: - // allocate more than len, to wrap with an offset and test the impact - byte[] array = new byte[size + 1024]; - data = java.nio.ByteBuffer.wrap(array, 100, size); - System.out.println("Running throughput test for payload of " + size + " bytes from a wrapped ByteBuffer"); - break; - } - - int posInit = data.position(); - for (int i = 0; i < size; ++i) { - data.put((byte) (i % 10)); - } - data.flip(); - data.position(posInit); - - try { - Path p = new Path(path); - - Value v = new RawValue(data); - - System.out.println("Login to Zenoh (locator=" + locator + ")..."); - Zenoh z = Zenoh.login(locator); - - Workspace w = z.workspace(); - - System.out.println("Put on " + p + " : " + data.remaining() + "b"); - - while (true) { - w.put(p, v); - } - - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZPutThr()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh/src/main/java/ZRemove.java b/examples/zenoh/src/main/java/ZRemove.java deleted file mode 100644 index 1fbe8305..00000000 --- a/examples/zenoh/src/main/java/ZRemove.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -import org.eclipse.zenoh.*; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -public class ZRemove implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-p", "--path"}, - description = "the path representing the URI.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/java/put/hello"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Override - public void run() { - try { - Path p = new Path(path); - - System.out.println("Login to Zenoh (locator=" + locator + ")..."); - Zenoh z = Zenoh.login(locator); - - Workspace w = z.workspace(); - - System.out.println("Remove " + p); - w.remove(p); - - z.logout(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZRemove()).execute(args); - System.exit(exitCode); - } -} \ No newline at end of file diff --git a/examples/zenoh/src/main/java/ZSub.java b/examples/zenoh/src/main/java/ZSub.java deleted file mode 100644 index abcea5b0..00000000 --- a/examples/zenoh/src/main/java/ZSub.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -import org.eclipse.zenoh.*; - -import java.io.InputStreamReader; -import java.util.List; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -public class ZSub implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-s", "--selector"}, - description = "The selector specifying the subscription.\n [default: ${DEFAULT-VALUE}]") - private String selector = "/zenoh/examples/**"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - @Override - public void run() { - try { - Selector s = new Selector(selector); - - System.out.println("Login to Zenoh (locator=" + locator + ")..."); - Zenoh z = Zenoh.login(locator); - - Workspace w = z.workspace(); - - System.out.println("Subscribe on " + selector); - SubscriptionId subid = w.subscribe(s, new Listener() { - public void onChanges(List changes) { - for (Change c : changes) { - switch (c.getKind()) { - case PUT: - System.out.printf(">> [Subscription listener] Received PUT on '%s': '%s')\n", c.getPath(), - c.getValue()); - break; - case UPDATE: - System.out.printf(">> [Subscription listener] Received UPDATE on '%s': '%s')\n", - c.getPath(), c.getValue()); - break; - case REMOVE: - System.out.printf(">> [Subscription listener] Received REMOVE on '%s')\n", c.getPath()); - break; - default: - System.err.printf( - ">> [Subscription listener] Received unkown operation with kind '%s' on '%s')\n", - c.getKind(), c.getPath()); - break; - } - } - } - }); - - System.out.println("Enter 'q' to quit...\n"); - InputStreamReader stdin = new InputStreamReader(System.in); - while ((char) stdin.read() != 'q') - ; - - w.unsubscribe(subid); - z.logout(); - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZSub()).execute(args); - System.exit(exitCode); - } -} \ No newline at end of file diff --git a/examples/zenoh/src/main/java/ZSubThr.java b/examples/zenoh/src/main/java/ZSubThr.java deleted file mode 100644 index 0d0a4d8e..00000000 --- a/examples/zenoh/src/main/java/ZSubThr.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -import java.util.List; - -import org.eclipse.zenoh.Change; -import org.eclipse.zenoh.Listener; -import org.eclipse.zenoh.Selector; -import org.eclipse.zenoh.Workspace; -import org.eclipse.zenoh.Zenoh; - -import picocli.CommandLine; -import picocli.CommandLine.Option; - -class ZSubThr implements Runnable { - - @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help message") - private boolean helpRequested = false; - - @Option(names = {"-p", "--path"}, - description = "The subscriber path.\n [default: ${DEFAULT-VALUE}]") - private String path = "/zenoh/examples/throughput/data"; - - @Option(names = {"-l", "--locator"}, - description = "The locator to be used to boostrap the zenoh session. By default dynamic discovery is used") - private String locator = null; - - private static final long N = 50000; - private static long count = 0; - private static long start; - private static long stop; - - public static void printStats(long start, long stop) { - float delta = stop - start; - float thpt = N / (delta / 1000); - System.out.format("%f msgs/sec%n", thpt); - } - - private static class Observer implements Listener { - public void onChanges(List changes) { - if (count == 0) { - start = System.currentTimeMillis(); - count++; - } else if (count < N) { - count++; - } else { - stop = System.currentTimeMillis(); - printStats(start, stop); - System.gc(); - count = 0; - } - } - } - - @Override - public void run() { - try { - Selector selector = new Selector(path); - - System.out.println("Login to Zenoh (locator=" + locator + ")..."); - Zenoh z = Zenoh.login(locator); - - Workspace w = z.workspace(); - - System.out.println("Subscribe on " + selector); - w.subscribe(selector, new Observer()); - - Thread.sleep(60000); - - } catch (Throwable e) { - e.printStackTrace(); - } - } - - public static void main(String[] args) { - int exitCode = new CommandLine(new ZSubThr()).execute(args); - System.exit(exitCode); - } -} diff --git a/examples/zenoh/src/main/resources/logback.xml b/examples/zenoh/src/main/resources/logback.xml deleted file mode 100644 index b7ae2de2..00000000 --- a/examples/zenoh/src/main/resources/logback.xml +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - true - - - - - - - - - - %highlight(%d{HH:mm:ss.SSS} %.-1level [%-18.18thread] %-20.20logger : %msg%n) - - - - - - - - - - - - diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..ec9df8fa --- /dev/null +++ b/gradle.properties @@ -0,0 +1,2 @@ +gpr.user=DariusIMP +gpr.key=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 diff --git a/jvm.png b/jvm.png new file mode 100644 index 00000000..07f1b123 Binary files /dev/null and b/jvm.png differ diff --git a/parent-pom/pom.xml b/parent-pom/pom.xml deleted file mode 100644 index 31c73d94..00000000 --- a/parent-pom/pom.xml +++ /dev/null @@ -1,239 +0,0 @@ - - - - 4.0.0 - - org.eclipse.zenoh - parent-pom - 0.4.2-M1 - pom - - Zenoh parent-pom - Parent POM for Zenoh - http://zenoh.io - - - - Eclipse Public License 2.0 - https://www.eclipse.org/legal/epl-2.0/ - - - Apache-2.0 - http://www.apache.org/licenses/LICENSE-2.0 - - - - - https://github.com/eclipse-zenoh/zenoh-java - scm:git:git://github.com/eclipse-zenoh/zenoh-java.git - scm:git:git@github.com:eclipse-zenoh/zenoh-java.git - HEAD - - - - Eclipse Foundation - http://www.eclipse.org/ - - - - - ADLINK zenoh team - zenoh@adlink-labs.tech - ADLINK Technology - - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - - - UTF-8 - 1.8 - 1.8 - - 1.7.25 - 2.3.4 - 4.12 - 1.2.3 - - 2.22.0 - 3.0.0-M3 - 1.6.0 - 3.0.0 - 3.1.0 - 3.1.0 - 0.8.5 - 1.6.8 - 1.6 - - - - - - - org.slf4j - slf4j-api - ${slf4j-version} - - - - - org.scijava - native-lib-loader - ${native-lib-loader-version} - - - - - junit - junit - ${junit-version} - test - - - ch.qos.logback - logback-core - ${logback-version} - test - - - ch.qos.logback - logback-classic - ${logback-version} - test - - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin-version} - - - org.apache.maven.plugins - maven-failsafe-plugin - ${maven-failsafe-plugin-version} - - - org.codehaus.mojo - exec-maven-plugin - ${exec-maven-plugin-version} - - - org.codehaus.mojo - build-helper-maven-plugin - ${build-helper-maven-plugin-version} - - - org.apache.maven.plugins - maven-resources-plugin - ${maven-resources-plugin-version} - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin-version} - - - org.jacoco - jacoco-maven-plugin - ${jacoco-maven-plugin-version} - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin-version} - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${nexus-staging-maven-plugin-version} - - ossrh - https://oss.sonatype.org/ - true - - - - - - - - - - jipp - - - jipp - - - - - - - org.apache.maven.plugins - maven-gpg-plugin - - - --pinentry-mode - loopback - - - - - - - - - - - release - - - - - org.apache.maven.plugins - maven-gpg-plugin - - - sign-artifacts - verify - - sign - - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - true - - - - - - - diff --git a/pom.xml b/pom.xml deleted file mode 100644 index f766b3b5..00000000 --- a/pom.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - 4.0.0 - - - org.eclipse.zenoh - parent-pom - parent-pom - 0.4.2-M1 - - - zenoh-java - pom - - Zenoh main pom - Zenoh-java build POM - - - parent-pom - zenoh - examples - - - diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 00000000..0834888f --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +1.72.0 diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..2d17a6a4 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,30 @@ +// +// 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, +// + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + google() + } +} +rootProject.name = "zenoh-java" + +include(":zenoh-java") +include(":examples") +include(":zenoh-jni") + +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version("0.4.0") +} diff --git a/zenoh-java/build.gradle.kts b/zenoh-java/build.gradle.kts new file mode 100644 index 00000000..684ec25d --- /dev/null +++ b/zenoh-java/build.gradle.kts @@ -0,0 +1,177 @@ +// +// 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, +// + +group = "io.zenoh" +version = "0.11.0-dev" + +plugins { + id("com.android.library") + kotlin("multiplatform") + kotlin("plugin.serialization") + id("com.adarshr.test-logger") + id("org.jetbrains.dokka") + id("org.mozilla.rust-android-gradle.rust-android") + `maven-publish` +} + +android { + namespace = "io.zenoh" + compileSdk = 30 + + ndkVersion = "26.0.10792818" + + defaultConfig { + minSdk = 30 + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + buildTypes { + getByName("release") { + isMinifyEnabled = false + } + getByName("debug") { + isMinifyEnabled = false + } + } + sourceSets { + getByName("main") { + manifest.srcFile("src/androidMain/AndroidManifest.xml") + } + } + publishing { + singleVariant("release") { + withSourcesJar() + withJavadocJar() + } + } +} + +cargo { + pythonCommand = "python3" + module = "../zenoh-jni" + libname = "zenoh-jni" + targetIncludes = arrayOf("libzenoh_jni.so") + targetDirectory = "../zenoh-jni/target/" + profile = "release" + targets = arrayListOf( + "arm", + "arm64", + "x86", + "x86_64", + ) +} + +kotlin { + jvmToolchain(11) + jvm { + compilations.all { + kotlinOptions.jvmTarget = "11" + } + testRuns["test"].executionTask.configure { + val zenohPaths = "../zenoh-jni/target/debug" + jvmArgs("-Djava.library.path=$zenohPaths") + } + } + androidTarget { + publishLibraryVariants("release") + } + + @Suppress("Unused") + sourceSets { + val commonMain by getting { + dependencies { + implementation("commons-net:commons-net:3.9.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + val androidUnitTest by getting { + dependencies { + implementation(kotlin("test-junit")) + } + } + val jvmMain by getting { + // The line below is intended to load the native libraries that are crosscompiled on GitHub actions when publishing a JVM package. + resources.srcDir("../jni-libs").include("*/**") + } + val jvmTest by getting { + resources.srcDir("../zenoh-jni/target/debug").include(arrayListOf("*.dylib", "*.so", "*.dll")) + } + } + + publishing { + repositories { + maven { + name = "GithubPackages" + url = uri("https://maven.pkg.github.com/eclipse-zenoh/zenoh-java") + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } + } +} + +tasks.withType { + doFirst { + // The line below is added for the Android Unit tests which are equivalent to the JVM tests. + // For them to work we need to specify the path to the native library as a system property and not as a jvmArg. + systemProperty("java.library.path", "../zenoh-jni/target/debug") + } +} + +tasks.whenObjectAdded { + if ((this.name == "mergeDebugJniLibFolders" || this.name == "mergeReleaseJniLibFolders")) { + this.dependsOn("cargoBuild") + this.inputs.dir(buildDir.resolve("rustJniLibs/android")) + } +} + +fun buildZenohJNI(mode: BuildMode = BuildMode.DEBUG) { + val cargoCommand = mutableListOf("cargo", "build") + + if (mode == BuildMode.RELEASE) { + cargoCommand.add("--release") + } + + val result = project.exec { + commandLine(*(cargoCommand.toTypedArray()), "--manifest-path", "../zenoh-jni/Cargo.toml") + } + + if (result.exitValue != 0) { + throw GradleException("Failed to build Zenoh-JNI.") + } +} + +enum class BuildMode { + DEBUG { + override fun toString(): String { + return "debug" + } + }, + RELEASE { + override fun toString(): String { + return "release" + } + } +} diff --git a/zenoh-java/src/androidMain/AndroidManifest.xml b/zenoh-java/src/androidMain/AndroidManifest.xml new file mode 100644 index 00000000..4fb03756 --- /dev/null +++ b/zenoh-java/src/androidMain/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/zenoh-java/src/androidMain/kotlin/io.zenoh/Zenoh.kt b/zenoh-java/src/androidMain/kotlin/io.zenoh/Zenoh.kt new file mode 100644 index 00000000..c8d9ad08 --- /dev/null +++ b/zenoh-java/src/androidMain/kotlin/io.zenoh/Zenoh.kt @@ -0,0 +1,41 @@ +// +// 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 + +/** + * Static singleton class to load the Zenoh native library once and only once, as well as the logger in function of the + * log level configuration. + */ +internal actual class Zenoh private actual constructor() { + + actual companion object { + private const val ZENOH_LIB_NAME = "zenoh_jni" + private const val ZENOH_LOGS_PROPERTY = "zenoh.logger" + + private var instance: Zenoh? = null + + actual fun load() { + instance ?: Zenoh().also { instance = it } + } + } + + init { + System.loadLibrary(ZENOH_LIB_NAME) + val logLevel = System.getProperty(ZENOH_LOGS_PROPERTY) + if (logLevel != null) { + Logger.start(logLevel) + } + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/Config.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/Config.kt new file mode 100644 index 00000000..2e2964a7 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/Config.kt @@ -0,0 +1,69 @@ +// +// 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 java.io.File +import java.nio.file.Path +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonElement + + +/** + * Config class to set the Zenoh configuration to be used through a [Session]. + * + * @property path The path to the configuration file. + * @constructor Create empty Config + */ +class Config private constructor(internal val path: Path? = null, internal val jsonConfig: JsonElement? = null) { + + companion object { + + /** + * Loads the default zenoh configuration. + */ + fun default(): Config { + return Config() + } + + /** + * Loads the configuration from the [File] specified. + * + * @param file The zenoh config file. + */ + fun from(file: File): Config { + return Config(file.toPath()) + } + + /** + * Loads the configuration from the [Path] specified. + * + * @param path The zenoh config file path. + */ + fun from(path: Path): Config { + return Config(path) + } + + /** + * Loads the configuration from the [json] specified. + * + * @param json The zenoh raw zenoh config. + */ + fun from(json: String): Config { + return Config(jsonConfig = Json.decodeFromString(json)) + } + } + + constructor(jsonConfig: JsonElement) : this(null, jsonConfig = jsonConfig) +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/Logger.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/Logger.kt new file mode 100644 index 00000000..db25cf2f --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/Logger.kt @@ -0,0 +1,27 @@ +// +// 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 + +/** Logger class to redirect the Rust logs from Zenoh to the kotlin environment. */ +class Logger { + + companion object { + /** + * Redirects the rust logs either to logcat for Android systems or to the standard output (for non-android + * systems). @param logLevel must be either "info", "debug", "warn", "trace" or "error". + */ + external fun start(logLevel: String) + } +} \ No newline at end of file diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/Resolvable.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/Resolvable.kt new file mode 100644 index 00000000..bc0012fe --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/Resolvable.kt @@ -0,0 +1,23 @@ +// +// 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 + +/** + * A resolvable function interface meant to be used by Zenoh builders. + */ +fun interface Resolvable { + + fun res(): R +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/Session.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/Session.kt new file mode 100644 index 00000000..9509ba8a --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/Session.kt @@ -0,0 +1,415 @@ +// +// 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.exceptions.SessionException +import io.zenoh.exceptions.ZenohException +import io.zenoh.handlers.Callback +import io.zenoh.jni.JNISession +import io.zenoh.keyexpr.KeyExpr +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.Sample +import io.zenoh.selector.Selector +import io.zenoh.subscriber.Reliability +import io.zenoh.subscriber.Subscriber +import io.zenoh.value.Value +import java.time.Duration +import java.util.* +import java.util.concurrent.BlockingQueue + +/** + * A Zenoh Session, the core interaction point with a Zenoh network. + * + * A session is typically associated with declarations such as [Publisher]s, [Subscriber]s, or [Queryable]s, which are + * declared using [declarePublisher], [declareSubscriber], and [declareQueryable], respectively. + * Other operations such as simple Put, Get or Delete can be performed from a session using [put], [get] and [delete]. + * Finally, it's possible to declare key expressions ([KeyExpr]) as well. + * + * Sessions are open upon creation and can be closed manually by calling [close]. Alternatively, the session will be + * automatically closed when used with Java's try-with-resources statement or its Kotlin counterpart, [use]. + * + * For optimal performance and adherence to good practices, it is recommended to have only one running session, which + * is sufficient for most use cases. You should _never_ construct one session per publisher/subscriber, as this will + * significantly increase the size of your Zenoh network, while preventing potential locality-based optimizations. + */ +class Session private constructor(private val config: Config) : AutoCloseable { + + private var jniSession: JNISession? = JNISession() + + companion object { + + private val sessionClosedException = SessionException("Session is closed.") + + /** + * Open a [Session] with the default [Config]. + * + * @return The opened [Session]. + * @throws [SessionException] in the case of a failure. + */ + @JvmStatic + @Throws(SessionException::class) + fun open(): Session { + val session = Session(Config.default()) + return session.launch() + } + + /** + * Open a [Session] with the provided [Config]. + * + * @param config The configuration for the session. + * @return The opened [Session]. + * @throws [SessionException] in the case of a failure. + */ + @JvmStatic + @Throws(SessionException::class) + fun open(config: Config): Session { + val session = Session(config) + return session.launch() + } + } + + init { + Zenoh.load() + } + + /** + * Close the session. + * + * Closing the session invalidates any attempt to perform a declaration or to perform an operation such as Put or Delete. + * Attempting to do so will result in a failure. + * + * However, any session declaration that was still alive and bound to the session previous to closing it, will still be alive. + */ + @Throws(SessionException::class) + override fun close() { + jniSession?.close() + jniSession = null + } + + protected fun finalize() { + jniSession?.close() + } + + /** + * Declare a [Publisher] on the session. + * + * Example: + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/greeting")) { + * try (Publisher publisher = session.declarePublisher(keyExpr) + * .priority(Priority.REALTIME) + * .congestionControl(CongestionControl.DROP) + * .res()) { + * int idx = 0; + * while (true) { + * String payload = "Hello for the " + idx + "th time!"; + * publisher.put(payload).res(); + * Thread.sleep(1000); + * idx++; + * } + * } + * } + * } + * ``` + * + * @param keyExpr The [KeyExpr] the publisher will be associated to. + * @return A resolvable [Publisher.Builder] + */ + fun declarePublisher(keyExpr: KeyExpr): Publisher.Builder = Publisher.Builder(this, keyExpr) + + /** + * Declare a [Subscriber] on the session. + * + * The default receiver is a [BlockingQueue], but can be changed with the [Subscriber.Builder.with] functions. + * + * Example: + * + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/sub")) { + * try (Subscriber>> subscriber = session.declareSubscriber(keyExpr).res()) { + * BlockingQueue> receiver = subscriber.getReceiver(); + * assert receiver != null; + * while (true) { + * Optional sample = receiver.take(); + * if (sample.isEmpty()) { + * break; + * } + * System.out.println(sample.get()); + * } + * } + * } + * } + * ``` + * + * @param keyExpr The [KeyExpr] the subscriber will be associated to. + * @return A [Subscriber.Builder] with a [BlockingQueue] receiver. + */ + fun declareSubscriber(keyExpr: KeyExpr): Subscriber.Builder>> = Subscriber.newBuilder(this, keyExpr) + + /** + * Declare a [Queryable] on the session. + * + * The default receiver is a [BlockingQueue], but can be changed with the [Queryable.Builder.with] functions. + * + * Example: + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/greeting")) { + * System.out.println("Declaring Queryable"); + * try (Queryable>> queryable = session.declareQueryable(keyExpr).res()) { + * BlockingQueue> receiver = queryable.getReceiver(); + * while (true) { + * Optional wrapper = receiver.take(); + * if (wrapper.isEmpty()) { + * break; + * } + * Query query = wrapper.get(); + * System.out.println("Received query at " + query.getSelector()); + * query.reply(keyExpr) + * .success("Hello!") + * .withKind(SampleKind.PUT) + * .withTimeStamp(TimeStamp.getCurrentTime()) + * .res(); + * } + * } + * } + * } + * ``` + * + * @param keyExpr The [KeyExpr] the queryable will be associated to. + * @return A [Queryable.Builder] with a [BlockingQueue] receiver. + */ + fun declareQueryable(keyExpr: KeyExpr): Queryable.Builder>> = Queryable.newBuilder(this, keyExpr) + + /** + * Declare a [KeyExpr]. + * + * Informs Zenoh that you intend to use the provided Key Expression repeatedly. + * + * It is generally not needed to declare key expressions, as declaring a subscriber, + * a queryable, or a publisher will also inform Zenoh of your intent to use their + * key expressions repeatedly. + * + * Example: + * ```java + * try (Session session = session.open()) { + * try (KeyExpr keyExpr = session.declareKeyExpr("demo/java/example").res()) { + * Publisher publisher = session.declarePublisher(keyExpr).res(); + * // ... + * } + * } + * ``` + * + * @param keyExpr The intended Key expression. + * @return A resolvable returning an optimized representation of the passed `keyExpr`. + */ + fun declareKeyExpr(keyExpr: String): Resolvable = Resolvable { + return@Resolvable jniSession?.run { + declareKeyExpr(keyExpr) + } ?: throw sessionClosedException + } + + /** + * Undeclare a [KeyExpr]. + * + * The key expression must have been previously declared on the session with [declareKeyExpr], + * otherwise the operation will result in a failure. + * + * @param keyExpr The key expression to undeclare. + * @return A resolvable returning the status of the undeclare operation. + */ + fun undeclare(keyExpr: KeyExpr): Resolvable = Resolvable { + return@Resolvable jniSession?.run { + undeclareKeyExpr(keyExpr) + } ?: throw (sessionClosedException) + } + + /** + * Declare a [Get] with a [BlockingQueue] receiver. + * + * ```java + * try (Session session = Session.open()) { + * try (Selector selector = Selector.tryFrom("demo/java/example")) { + * session.get(selector) + * .consolidation(ConsolidationMode.NONE) + * .withValue("Get value example") + * .with(reply -> System.out.println("Received reply " + reply)) + * .res() + * } + * } + * ``` + * + * @param selector The [KeyExpr] to be used for the get operation. + * @return a resolvable [Get.Builder] with a [BlockingQueue] receiver. + */ + fun get(selector: Selector): Get.Builder>> = Get.newBuilder(this, selector) + + /** + * Declare a [Get] with a [BlockingQueue] receiver as default. + * + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/java/example")) { + * session.get(keyExpr) + * .consolidation(ConsolidationMode.NONE) + * .withValue("Get value example") + * .with(reply -> System.out.println("Received reply " + reply)) + * .res() + * } + * } + * ``` + * + * @param keyExpr The [KeyExpr] to be used for the get operation. + * @return a resolvable [Get.Builder] with a [BlockingQueue] receiver. + */ + fun get(keyExpr: KeyExpr): Get.Builder>> = Get.newBuilder(this, Selector(keyExpr)) + + /** + * Declare a [Put] with the provided value on the specified key expression. + * + * Example: + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/greeting")) { + * session.put(keyExpr, Value("Hello!")) + * .congestionControl(CongestionControl.BLOCK) + * .priority(Priority.REALTIME) + * .kind(SampleKind.PUT) + * .res(); + * System.out.println("Put 'Hello' on " + keyExpr + "."); + * } + * } + * ``` + * + * @param keyExpr The [KeyExpr] to be used for the put operation. + * @param value The [Value] to be put. + * @return A resolvable [Put.Builder]. + */ + fun put(keyExpr: KeyExpr, value: Value): Put.Builder = Put.newBuilder(this, keyExpr, value) + + /** + * Declare a [Put] with the provided value on the specified key expression. + * + * Example: + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/greeting")) { + * session.put(keyExpr, "Hello!") + * .congestionControl(CongestionControl.BLOCK) + * .priority(Priority.REALTIME) + * .kind(SampleKind.PUT) + * .res(); + * System.out.println("Put 'Hello' on " + keyExpr + "."); + * } + * } + * ``` + * + * @param keyExpr The [KeyExpr] to be used for the put operation. + * @param message The message to be put. + * @return A resolvable [Put.Builder]. + */ + fun put(keyExpr: KeyExpr, message: String): Put.Builder = Put.newBuilder(this, keyExpr, Value(message)) + + /** + * Declare a [Delete]. + * + * Example: + * + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example")) { + * session.delete(keyExpr).res(); + * System.out.println("Performed delete on " + keyExpr + "."); + * } + * } + * ``` + * + * @param keyExpr The [KeyExpr] to be used for the delete operation. + * @return a resolvable [Delete.Builder]. + */ + fun delete(keyExpr: KeyExpr): Delete.Builder = Delete.newBuilder(this, keyExpr) + + /** Returns if session is open or has been closed. */ + fun isOpen(): Boolean { + return jniSession != null + } + + @Throws(SessionException::class) + internal fun resolvePublisher(builder: Publisher.Builder): Publisher { + return jniSession?.run { + declarePublisher(builder) + } ?: throw (sessionClosedException) + } + + @Throws(ZenohException::class) + internal fun resolveSubscriber( + keyExpr: KeyExpr, callback: Callback, onClose: () -> Unit, receiver: R?, reliability: Reliability + ): Subscriber { + return jniSession?.run { + declareSubscriber(keyExpr, callback, onClose, receiver, reliability) + } ?: throw (sessionClosedException) + } + + @Throws(ZenohException::class) + internal fun resolveQueryable( + keyExpr: KeyExpr, callback: Callback, onClose: () -> Unit, receiver: R?, complete: Boolean + ): Queryable { + return jniSession?.run { + declareQueryable(keyExpr, callback, onClose, receiver, complete) + } ?: throw (sessionClosedException) + } + + @Throws(ZenohException::class) + internal fun resolveGet( + selector: Selector, + callback: Callback, + onClose: () -> Unit, + receiver: R?, + timeout: Duration, + target: QueryTarget, + consolidation: ConsolidationMode, + value: Value? + ): R? { + if (jniSession == null) { + throw sessionClosedException + } + return jniSession?.performGet(selector, callback, onClose, receiver, timeout, target, consolidation, value) + } + + @Throws(ZenohException::class) + internal fun resolvePut(keyExpr: KeyExpr, put: Put) { + jniSession?.run { performPut(keyExpr, put) } + } + + @Throws(ZenohException::class) + internal fun resolveDelete(keyExpr: KeyExpr, delete: Delete) { + jniSession?.run { performPut(keyExpr, delete) } + } + + /** Launches the session through the jni session, returning the [Session] on success. */ + @Throws(SessionException::class) + private fun launch(): Session { + jniSession!!.open(config) + return this + } +} + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/SessionDeclaration.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/SessionDeclaration.kt new file mode 100644 index 00000000..1c747e40 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/SessionDeclaration.kt @@ -0,0 +1,30 @@ +// +// 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 + +/** + * Session declaration. + * + * A session declaration is either a [io.zenoh.publication.Publisher], + * a [io.zenoh.subscriber.Subscriber] or a [io.zenoh.queryable.Queryable] declared from a [Session]. + */ +interface SessionDeclaration { + + /** Returns true if the declaration has not been undeclared. */ + fun isValid(): Boolean + + /** Undeclare a declaration. No further operations should be performed after calling this function. */ + fun undeclare() +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/Zenoh.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/Zenoh.kt new file mode 100644 index 00000000..ee156130 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/Zenoh.kt @@ -0,0 +1,25 @@ +// +// 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 + +/** + * Static singleton class to load the Zenoh native library once and only once, as well as the logger in function of the + * log level configuration. + */ +internal expect class Zenoh private constructor() { + companion object { + fun load() + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/ZenohType.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/ZenohType.kt new file mode 100644 index 00000000..3089ea3c --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/ZenohType.kt @@ -0,0 +1,9 @@ +package io.zenoh + +/** + * Zenoh type. An empty interface to regroup elements of type [io.zenoh.sample.Sample], + * [io.zenoh.query.Reply] and [io.zenoh.queryable.Query]. + * + * This kind of elements have in common that they can be received through the Zenoh network. + */ +interface ZenohType diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/JNIException.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/JNIException.kt new file mode 100644 index 00000000..feea7a90 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/JNIException.kt @@ -0,0 +1,23 @@ +// +// 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.exceptions + +/** + * JNI (Java native interface) exception. + * + * 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() diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/KeyExprException.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/KeyExprException.kt new file mode 100644 index 00000000..5836e02c --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/KeyExprException.kt @@ -0,0 +1,24 @@ +// +// 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.exceptions + +/** + * Key expression exception. + * + * This kind of exceptions are thrown from the native code when something goes wrong regarding a key expression, + * for instance when attempting to create a [io.zenoh.keyexpr.KeyExpr] from a string that does not respect the + * key expression conventions. + */ +class KeyExprException(msg: String) : ZenohException(msg) diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/SessionException.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/SessionException.kt new file mode 100644 index 00000000..02027e0f --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/SessionException.kt @@ -0,0 +1,22 @@ +// +// 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.exceptions + +/** + * Session exception. + * + * This kind of exceptions are thrown from the native code when something goes wrong with a Zenoh session. + */ +class SessionException(message: String?) : ZenohException(message) diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/ZenohException.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/ZenohException.kt new file mode 100644 index 00000000..c94a2dac --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/exceptions/ZenohException.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.exceptions + +/** + * A Zenoh exception. + */ +abstract class ZenohException(override val message: String? = null) : Exception() diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/handlers/BlockingQueueHandler.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/handlers/BlockingQueueHandler.kt new file mode 100644 index 00000000..1bedd7fa --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/handlers/BlockingQueueHandler.kt @@ -0,0 +1,47 @@ +// +// 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.handlers + +import io.zenoh.ZenohType +import java.util.Optional +import java.util.concurrent.BlockingQueue + +/** + * Blocking queue handler + * + * Implementation of a [Handler] with a [BlockingQueue] receiver. This handler is intended to be used + * as the default handler by the [io.zenoh.queryable.Queryable], [io.zenoh.subscriber.Subscriber] and [io.zenoh.query.Get], + * allowing us to send the incoming elements through a [BlockingQueue]. + * + * The way to tell no more elements of type [T] will be received is when an empty element is put (see [onClose]). + * + * @param T a [ZenohType] + * @property queue + * @constructor Create empty Queue handler + */ +class BlockingQueueHandler(private val queue: BlockingQueue>) : Handler>> { + + override fun handle(t: T) { + queue.put(Optional.of(t)) + } + + override fun receiver(): BlockingQueue> { + return queue + } + + override fun onClose() { + queue.put(Optional.empty()) + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/handlers/Callback.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/handlers/Callback.kt new file mode 100644 index 00000000..02297a0d --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/handlers/Callback.kt @@ -0,0 +1,30 @@ +// +// 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.handlers + +import io.zenoh.ZenohType + +/** + * Runnable callback. + * + * @param T Type of the parameter, which is a [ZenohType]. + * @constructor Create empty Callback + */ +fun interface Callback { + + /** Callback to be run. */ + fun run(t: T) + +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/handlers/Handler.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/handlers/Handler.kt new file mode 100644 index 00000000..0647e852 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/handlers/Handler.kt @@ -0,0 +1,80 @@ +// +// 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.handlers + +import io.zenoh.ZenohType + +/** + * Handler interface for classes implementing behavior to handle the + * incoming [T] elements. + * + * **Example**: + * ```java + * public class QueueHandler implements Handler> { + * + * private final ArrayDeque queue = new ArrayDeque<>(); + * + * @Override + * public void handle(T t) { + * System.out.println("Received " + t + ", enqueuing..."); + * queue.add(t); + * } + * + * @Override + * public ArrayDeque receiver() { + * return queue; + * } + * + * @Override + * public void onClose() { + * System.out.println("Received in total " + queue.size() + " elements."); + * } + * } + * ``` + * + * That `QueueHandler` could then be used as follows, for instance for a subscriber: + * ```java + * QueueHandler handler = new QueueHandler(); + * session.declareSubscriber(keyExpr).with(handler).res(); + * ... + * ``` + * + * @param T A receiving [ZenohType], either a [io.zenoh.sample.Sample], a [io.zenoh.query.Reply] or a [io.zenoh.queryable.Query]. + * @param R An arbitrary receiver. + */ +interface Handler { + + /** + * Handle the received [t] element. + * + * @param t An element of type [T]. + */ + fun handle(t: T) + + /** + * Return the receiver of the handler. + */ + fun receiver(): R + + /** + * This callback is invoked by Zenoh at the conclusion of the handler's lifecycle. + * + * For instances of [io.zenoh.queryable.Queryable] and [io.zenoh.subscriber.Subscriber], + * Zenoh triggers this callback when they are closed or undeclared. In the case of a Get query + * (see [io.zenoh.query.Get]), it is invoked when no more elements of type [T] are expected + * to be received. + */ + fun onClose() +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt new file mode 100644 index 00000000..2e218949 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIKeyExpr.kt @@ -0,0 +1,91 @@ +// +// 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.Zenoh +import io.zenoh.exceptions.ZenohException +import io.zenoh.keyexpr.KeyExpr + +internal class JNIKeyExpr(internal val ptr: Long) { + + companion object { + @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)) + } + + @Throws(ZenohException::class) + fun autocanonize(keyExpr: String): KeyExpr { + Zenoh.load() + val keyExprPtr = autocanonizeViaJNI(keyExpr) + return KeyExpr(JNIKeyExpr(keyExprPtr)) + } + + @Throws(ZenohException::class) + private external fun tryFromViaJNI(keyExpr: String): Long + + @Throws(ZenohException::class) + private external fun autocanonizeViaJNI(keyExpr: String): Long + } + + override fun toString(): String { + return getStringValueViaJNI(ptr) + } + + fun intersects(other: KeyExpr): Boolean { + if (other.jniKeyExpr == null) { + return false + } + return intersectsViaJNI(ptr, other.jniKeyExpr!!.ptr) + } + + fun includes(other: KeyExpr): Boolean { + if (other.jniKeyExpr == null) { + return false + } + return includesViaJNI(ptr, other.jniKeyExpr!!.ptr) + } + + 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 new file mode 100644 index 00000000..fa2eebc2 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIPublisher.kt @@ -0,0 +1,120 @@ +// +// 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.* +import io.zenoh.exceptions.ZenohException +import io.zenoh.prelude.SampleKind +import io.zenoh.publication.CongestionControl +import io.zenoh.publication.Priority +import io.zenoh.value.Value + +/** + * Adapter class to handle the interactions with Zenoh through JNI for a [Publisher]. + * + * @property ptr: raw pointer to the underlying native Publisher. + */ +internal class JNIPublisher(private val ptr: Long) { + + /** + * Put value through the publisher. + * + * @param value The [Value] to be put. + */ + @Throws(ZenohException::class) + fun put(value: Value) { + putViaJNI(value.payload, value.encoding.knownEncoding.ordinal, ptr) + } + + @Throws(ZenohException::class) + fun write(kind: SampleKind, value: Value) { + writeViaJNI(value.payload, value.encoding.knownEncoding.ordinal, kind.ordinal, ptr) + } + + @Throws(ZenohException::class) + fun delete() { + deleteViaJNI(ptr) + } + + /** + * Close and free the underlying publisher pointer. + * + * Further operations with this publisher should not be performed anymore. + */ + fun close() { + 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, ptr: Long) + + @Throws(ZenohException::class) + private external fun writeViaJNI(payload: ByteArray, encoding: Int, sampleKind: Int, ptr: Long) + + @Throws(ZenohException::class) + private external fun deleteViaJNI(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 new file mode 100644 index 00000000..1b3feb63 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIQuery.kt @@ -0,0 +1,73 @@ +// +// 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.exceptions.ZenohException +import io.zenoh.sample.Sample +import io.zenoh.value.Value + +/** + * Adapter class for interacting with a Query using JNI. + * + * This class serves as an adapter for interacting with a Query through JNI (Java Native Interface). + * + * @property ptr The raw pointer to the underlying native query. + */ +internal class JNIQuery(private val ptr: Long) { + + @Throws(ZenohException::class) + fun replySuccess(sample: Sample) { + val timestampEnabled = sample.timestamp != null + replySuccessViaJNI( + ptr, + sample.keyExpr.jniKeyExpr!!.ptr, + sample.value.payload, + sample.value.encoding.knownEncoding.ordinal, + sample.kind.ordinal, + timestampEnabled, + if (timestampEnabled) sample.timestamp!!.ntpValue() else 0, + ) + } + + @Throws(ZenohException::class) + fun replyError(errorValue: Value) { + replyErrorViaJNI(ptr, errorValue.payload, errorValue.encoding.knownEncoding.ordinal) + } + + fun close() { + freePtrViaJNI(ptr) + } + + @Throws(ZenohException::class) + private external fun replySuccessViaJNI( + queryPtr: Long, + keyExpr: Long, + valuePayload: ByteArray, + valueEncoding: Int, + sampleKind: Int, + timestampEnabled: Boolean, + timestampNtp64: Long + ) + + @Throws(ZenohException::class) + private external fun replyErrorViaJNI( + queryPtr: Long, + errorValuePayload: ByteArray, + errorValueEncoding: Int, + ) + + /** Frees the underlying native Query. */ + private external fun freePtrViaJNI(ptr: Long) +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIQueryable.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIQueryable.kt new file mode 100644 index 00000000..f17df868 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNIQueryable.kt @@ -0,0 +1,30 @@ +// +// 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 + +/** + * Adapter class to handle the interactions with Zenoh through JNI for a [Queryable] + * + * @property ptr: raw pointer to the underlying native Queryable. + */ +internal class JNIQueryable(val ptr: Long) { + + fun close() { + freePtrViaJNI(ptr) + } + + /** Frees the underlying native Queryable. */ + private external fun freePtrViaJNI(ptr: Long) +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt new file mode 100644 index 00000000..e8f04732 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISession.kt @@ -0,0 +1,275 @@ +// +// 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.* +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.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.Sample +import io.zenoh.selector.Selector +import io.zenoh.subscriber.Reliability +import io.zenoh.subscriber.Subscriber +import io.zenoh.value.Value +import org.apache.commons.net.ntp.TimeStamp +import java.time.Duration +import java.util.concurrent.atomic.AtomicLong + +/** Adapter class to handle the communication with the Zenoh JNI code for a [Session]. */ +internal class JNISession { + + /* Pointer to the underlying Rust zenoh session. */ + private var sessionPtr: AtomicLong = AtomicLong(0) + + @Throws(SessionException::class) + fun open(config: Config) { + config.jsonConfig?.let { jsonConfig -> + sessionPtr.set(openSessionWithJsonConfigViaJNI(jsonConfig.toString())) + } ?: run { + sessionPtr.set(openSessionViaJNI(config.path?.toString().orEmpty())) + } + } + + fun close() { + closeSessionViaJNI(sessionPtr.get()) + } + + @Throws(ZenohException::class) + fun declarePublisher(builder: Publisher.Builder): Publisher { + val publisherRawPtr = declarePublisherViaJNI( + builder.keyExpr.jniKeyExpr!!.ptr, + sessionPtr.get(), + builder.congestionControl.ordinal, + builder.priority.value, + ) + return Publisher( + builder.keyExpr, + JNIPublisher(publisherRawPtr), + builder.congestionControl, + builder.priority, + ) + } + + @Throws(ZenohException::class) + fun declareSubscriber( + keyExpr: KeyExpr, callback: Callback, onClose: () -> Unit, receiver: R?, reliability: Reliability + ): Subscriber { + val subCallback = + JNISubscriberCallback { keyExprPtr, payload, encoding, kind, timestampNTP64, timestampIsValid -> + val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null + val sample = Sample( + KeyExpr(JNIKeyExpr(keyExprPtr)), + Value(payload, Encoding(KnownEncoding.fromInt(encoding))), + SampleKind.fromInt(kind), + timestamp + ) + callback.run(sample) + } + val subscriberRawPtr = declareSubscriberViaJNI( + keyExpr.jniKeyExpr!!.ptr, sessionPtr.get(), subCallback, onClose, reliability.ordinal + ) + return Subscriber(keyExpr, receiver, JNISubscriber(subscriberRawPtr)) + } + + @Throws(ZenohException::class) + fun declareQueryable( + keyExpr: KeyExpr, callback: Callback, onClose: () -> Unit, receiver: R?, complete: Boolean + ): Queryable { + val queryCallback = + JNIQueryableCallback { keyExprPtr: Long, selectorParams: String, withValue: Boolean, payload: ByteArray?, encoding: Int, 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 query = Query(keyExpression, selector, value, jniQuery) + callback.run(query) + } + val queryableRawPtr = + declareQueryableViaJNI(keyExpr.jniKeyExpr!!.ptr, sessionPtr.get(), queryCallback, onClose, complete) + return Queryable(keyExpr, receiver, JNIQueryable(queryableRawPtr)) + } + + @Throws(ZenohException::class) + fun performGet( + selector: Selector, + callback: Callback, + onClose: () -> Unit, + receiver: R?, + timeout: Duration, + target: QueryTarget, + consolidation: ConsolidationMode, + value: Value? + ): R? { + val getCallback = + JNIGetCallback { replierId: String, success: Boolean, keyExprPtr: Long, payload: ByteArray, encoding: Int, kind: Int, timestampNTP64: Long, timestampIsValid: Boolean -> + if (success) { + val timestamp = if (timestampIsValid) TimeStamp(timestampNTP64) else null + val sample = Sample( + KeyExpr(JNIKeyExpr(keyExprPtr)), + Value(payload, Encoding(KnownEncoding.fromInt(encoding))), + SampleKind.fromInt(kind), + timestamp + ) + val reply = Reply.Success(replierId, sample) + callback.run(reply) + } else { + val reply = Reply.Error(replierId, Value(payload, Encoding(KnownEncoding.fromInt(encoding)))) + callback.run(reply) + } + } + + if (value == null) { + getViaJNI( + selector.keyExpr.jniKeyExpr!!.ptr, + selector.parameters, + sessionPtr.get(), + getCallback, + onClose, + timeout.toMillis(), + target.ordinal, + consolidation.ordinal, + ) + } else { + getWithValueViaJNI( + selector.keyExpr.jniKeyExpr!!.ptr, + selector.parameters, + sessionPtr.get(), + getCallback, + onClose, + timeout.toMillis(), + target.ordinal, + consolidation.ordinal, + value.payload, + value.encoding.knownEncoding.ordinal, + ) + } + return receiver + } + + @Throws(ZenohException::class) + fun declareKeyExpr(keyExpr: String): KeyExpr { + val ptr = declareKeyExprViaJNI(sessionPtr.get(), keyExpr) + return KeyExpr(JNIKeyExpr(ptr)) + } + + @Throws(ZenohException::class) + fun undeclareKeyExpr(keyExpr: KeyExpr) { + undeclareKeyExprViaJNI(sessionPtr.get(), keyExpr.jniKeyExpr!!.ptr) + } + + @Throws(ZenohException::class) + fun performPut( + keyExpr: KeyExpr, + put: Put, + ) { + putViaJNI( + keyExpr.jniKeyExpr!!.ptr, + sessionPtr.get(), + put.value.payload, + put.value.encoding.knownEncoding.ordinal, + put.congestionControl.ordinal, + put.priority.value, + put.kind.ordinal, + ) + } + + @Throws(ZenohException::class) + private external fun openSessionViaJNI(configFilePath: String): Long + + @Throws(Exception::class) + private external fun openSessionWithJsonConfigViaJNI(jsonConfig: String): Long + + @Throws(Exception::class) + private external fun closeSessionViaJNI(ptr: Long) + + @Throws(ZenohException::class) + private external fun declarePublisherViaJNI( + keyExpr: Long, ptr: Long, congestionControl: Int, priority: Int + ): Long + + @Throws(ZenohException::class) + private external fun declareSubscriberViaJNI( + keyExpr: Long, + sessionPtr: Long, + callback: JNISubscriberCallback, + onClose: JNIOnCloseCallback, + reliability: Int + ): Long + + @Throws(ZenohException::class) + private external fun declareQueryableViaJNI( + keyExpr: Long, + sessionPtr: Long, + callback: JNIQueryableCallback, + onClose: JNIOnCloseCallback, + complete: Boolean + ): Long + + @Throws(ZenohException::class) + private external fun declareKeyExprViaJNI(sessionPtr: Long, keyExpr: String): Long + + @Throws(ZenohException::class) + private external fun undeclareKeyExprViaJNI(sessionPtr: Long, keyExprPtr: Long) + + @Throws(ZenohException::class) + private external fun getViaJNI( + keyExpr: Long, + selectorParams: String, + sessionPtr: Long, + callback: JNIGetCallback, + onClose: JNIOnCloseCallback, + timeoutMs: Long, + target: Int, + consolidation: Int, + ) + + @Throws(ZenohException::class) + private external fun getWithValueViaJNI( + keyExpr: Long, + selectorParams: String, + sessionPtr: Long, + callback: JNIGetCallback, + onClose: JNIOnCloseCallback, + timeoutMs: Long, + target: Int, + consolidation: Int, + payload: ByteArray, + encoding: Int + ) + + @Throws(ZenohException::class) + private external fun putViaJNI( + keyExpr: Long, + sessionPtr: Long, + valuePayload: ByteArray, + valueEncoding: Int, + congestionControl: Int, + priority: Int, + kind: Int, + ) +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISubscriber.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISubscriber.kt new file mode 100644 index 00000000..73bd2dad --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/JNISubscriber.kt @@ -0,0 +1,31 @@ +// +// 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 + +/** + * Adapter class to handle the interactions with Zenoh through JNI for a [io.zenoh.subscriber.Subscriber] + * + * @property ptr: raw pointer to the underlying native Subscriber. + */ +internal class JNISubscriber(private val ptr: Long) { + + fun close() { + freePtrViaJNI(ptr) + } + + /** Frees the underlying native Subscriber. */ + private external fun freePtrViaJNI(ptr: Long) + +} 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 new file mode 100644 index 00000000..5adb1653 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIGetCallback.kt @@ -0,0 +1,29 @@ +// +// 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.callbacks + +internal fun interface JNIGetCallback { + + fun run( + replierId: String, + success: Boolean, + keyExpr: Long, + payload: ByteArray, + encoding: Int, + kind: Int, + timestampNTP64: Long, + timestampIsValid: Boolean + ) +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIOnCloseCallback.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIOnCloseCallback.kt new file mode 100644 index 00000000..b58fa23d --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIOnCloseCallback.kt @@ -0,0 +1,21 @@ +// +// 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.callbacks + +internal fun interface JNIOnCloseCallback { + + fun run() + +} 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 new file mode 100644 index 00000000..402d1cf4 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNIQueryableCallback.kt @@ -0,0 +1,24 @@ +// +// 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.callbacks + +internal fun interface JNIQueryableCallback { + fun run(keyExprPtr: Long, + selectorParams: String, + withValue: Boolean, + payload: ByteArray?, + encoding: Int, + 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 new file mode 100644 index 00000000..fc0a4ca5 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/jni/callbacks/JNISubscriberCallback.kt @@ -0,0 +1,26 @@ +// +// 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.callbacks + +internal fun interface JNISubscriberCallback { + fun run( + keyExpr: Long, + payload: ByteArray, + encoding: Int, + kind: Int, + timestampNTP64: Long, + timestampIsValid: Boolean + ) +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/IntoKeyExpr.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/IntoKeyExpr.kt new file mode 100644 index 00000000..74ab2d80 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/IntoKeyExpr.kt @@ -0,0 +1,26 @@ +// +// 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.keyexpr + +import io.zenoh.exceptions.KeyExprException + +@Throws(KeyExprException::class) +fun String.intoKeyExpr(): KeyExpr { + if (this.isEmpty()) { + throw(KeyExprException("Attempting to create a KeyExpr from an empty string.")) + } + return KeyExpr.autocanonize(this) +} + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt new file mode 100644 index 00000000..e6494e14 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/keyexpr/KeyExpr.kt @@ -0,0 +1,167 @@ +// +// 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.keyexpr + +import io.zenoh.Resolvable +import io.zenoh.Session +import io.zenoh.exceptions.KeyExprException +import io.zenoh.jni.JNIKeyExpr +import kotlin.jvm.Throws + +/** + * # Address space + * + * Zenoh's address space is designed around keys which serve as the names of resources. + * + * Keys are slash-separated lists of non-empty UTF8 strings. They may not contain the following characters: `$*#?`. + * + * Zenoh's operations are executed on key expressions, a small language that allows the definition + * of sets of keys via the use of wildcards: + * + * - `*` is the single-chunk wildcard, and will match any chunk: `a/*/c` will match `a/b/c`, `a/hello/c`, etc... + * - `**` is the 0 or more chunks wildcard: `a/**/c` matches `a/c`, `a/b/c`, `a/b/hello/c`, etc... + * - `$*` is the sub-chunk wildcard, it will match any amount of non-/ characters: `a/b$*` matches `a/b`, `a/because`, `a/blue`... but not `a/c` nor `a/blue/c` + * + * To allow for better performance and gain the property that two key expressions define the same + * set if and only if they are the same string, the rules of canon form are mandatory for a key + * expression to be propagated by a Zenoh network: + * + * - `**/**` may not exist, as it could always be replaced by the shorter `**`, + * - `** /*` may not exist, and must be written as its equivalent `*/**` instead, + * - `$*` may not exist alone in a chunk, as it must be written `*` instead. + * + * The `KeyExpr.autocanonize` constructor exists to correct eventual infringements of the canonization rules. + * + * A KeyExpr is a string that has been validated to be a valid Key Expression. + * + * # Memory + * + * 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. + * + * @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. + */ +class KeyExpr internal constructor(internal var jniKeyExpr: JNIKeyExpr? = null): AutoCloseable { + + companion object { + + /** + * Try from. + * + * The default way to construct a KeyExpr. This function will ensure that the passed expression is valid. + * It won't however try to correct expressions that aren't canon. + * + * You may use [autocanonize] instead if you are unsure if the expression you will use for construction will be canon. + * + * @param keyExpr The intended key expression as a string. + * @return The [KeyExpr] in case of success. + * @throws KeyExprException in the case of failure. + */ + @JvmStatic + @Throws(KeyExprException::class) + fun tryFrom(keyExpr: String): KeyExpr { + return JNIKeyExpr.tryFrom(keyExpr) + } + + /** + * Autocanonize. + * + * This alternative constructor for key expressions will attempt to canonize the passed + * expression before checking if it is valid. + * + * @param keyExpr The intended key expression as a string. + * @return The canonized [KeyExpr]. + * @throws KeyExprException in the case of failure. + */ + @JvmStatic + @Throws(KeyExprException::class) + fun autocanonize(keyExpr: String): KeyExpr { + return JNIKeyExpr.autocanonize(keyExpr) + } + } + + /** + * Intersects operation. This method returns `True` if there exists at least one key that belongs to both sets + * defined by `this` and `other`. + * Will return false as well if the key expression is not valid anymore. + */ + fun intersects(other: KeyExpr): Boolean { + return jniKeyExpr?.intersects(other) ?: false + } + + /** + * Includes operation. This method returns `true` when all the keys defined by `other` also belong to the set + * defined by `this`. + * Will return false as well if the key expression is not valid anymore. + */ + fun includes(other: KeyExpr): Boolean { + return jniKeyExpr?.includes(other) ?: false + } + + /** + * Undeclare the key expression if it was previously declared on the specified [session]. + * + * @param session The session from which the key expression was previously declared. + * @return An empty [Resolvable]. + */ + fun undeclare(session: Session): Resolvable { + return session.undeclare(this) + } + + /** + * Returns true if the [KeyExpr] has still associated a native key expression allowing it to perform operations. + */ + fun isValid(): Boolean { + return jniKeyExpr != null + } + + override fun toString(): String { + return this.jniKeyExpr?.toString() ?: "" + } + + /** + * Closes the key expression. Operations performed on this key expression won't be valid anymore after this call. + */ + override fun close() { + jniKeyExpr?.close() + 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 + } + + override fun hashCode(): Int { + return jniKeyExpr?.hashCode() ?: 0 + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Encoding.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Encoding.kt new file mode 100644 index 00000000..b466a724 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/Encoding.kt @@ -0,0 +1,79 @@ +// +// 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 + +/** + * The encoding of a [io.zenoh.value.Value]. + * + * A zenoh encoding is an HTTP Mime type and a string suffix. + * + * **Suffixes are not yet supported by zenoh-jni and are currently ignored.** + * + * @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, "") + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Encoding + + if (knownEncoding != other.knownEncoding) return false + return suffix == other.suffix + } + + override fun hashCode(): Int { + var result = knownEncoding.hashCode() + result = 31 * result + suffix.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 } + + fun default() = EMPTY + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/SampleKind.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/SampleKind.kt new file mode 100644 index 00000000..6e057c50 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/prelude/SampleKind.kt @@ -0,0 +1,25 @@ +// +// 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 + +/** The kind of sample. */ +enum class SampleKind { + PUT, + DELETE; + + companion object { + fun fromInt(value: Int) = entries.first { it.ordinal == value } + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/CongestionControl.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/CongestionControl.kt new file mode 100644 index 00000000..3662e542 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/CongestionControl.kt @@ -0,0 +1,30 @@ +// +// 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.publication + +/** The congestion control to be applied when routing the data. */ +enum class CongestionControl { + + /** + * 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, + + /** + * Allows the message to be dropped if all buffers are full. + */ + DROP; +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Delete.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Delete.kt new file mode 100644 index 00000000..3d6702df --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Delete.kt @@ -0,0 +1,102 @@ +// +// 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.publication + +import io.zenoh.prelude.SampleKind +import io.zenoh.Session +import io.zenoh.exceptions.ZenohException +import io.zenoh.value.Value +import io.zenoh.keyexpr.KeyExpr +import kotlin.jvm.Throws + +/** + * Delete operation to perform on Zenoh on a key expression. + * + * Example: + * ```java + * public void deleteExample() throws ZenohException { + * System.out.println("Opening session..."); + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/java/example")) { + * session.delete(keyExpr).res(); + * System.out.println("Performed a delete on '" + keyExpr); + * } + * } + * } + * ``` + * + * A delete operation is a special case of a Put operation, it is analogous to perform a Put with an empty value and + * 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) { + + companion object { + /** + * Creates a new [Builder] associated with the specified [session] and [keyExpr]. + * + * @param session The [Session] from which the Delete will be performed. + * @param keyExpr The [KeyExpr] upon which the Delete will be performed. + * @return A [Delete] operation [Builder]. + */ + fun newBuilder(session: Session, keyExpr: KeyExpr): Builder { + return Builder(session, keyExpr) + } + } + + /** + * Builder to construct a [Delete] operation. + * + * @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, + ) { + + /** Change the [CongestionControl] to apply when routing the data. */ + fun congestionControl(congestionControl: CongestionControl) = + apply { this.congestionControl = congestionControl } + + /** Change the [Priority] of the written data. */ + fun priority(priority: Priority) = apply { this.priority = priority } + + /** + * 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 + * was properly executed remotely. + */ + @Throws(ZenohException::class) + fun res() { + val delete = Delete( + this.keyExpr, Value.empty(), this.congestionControl, this.priority, SampleKind.DELETE + ) + session.resolveDelete(keyExpr, delete) + } + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Priority.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Priority.kt new file mode 100644 index 00000000..a8821fca --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Priority.kt @@ -0,0 +1,34 @@ +// +// 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.publication + +/** + * The Priority of Zenoh messages. + * + * A Priority is identified by a numeric value. Lower the value, higher the priority. Higher the value, lower the priority. + * + * - Highest priority: 1 ([REALTIME]) + * - Lowest priority: 7 ([BACKGROUND]) + */ +enum class Priority(val value: Int) { + REALTIME(1), + INTERACTIVE_HIGH(2), + INTERACTIVE_LOW(3), + DATA_HIGH(4), + DATA(5), + DATA_LOW(6), + BACKGROUND(7); +} + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Publisher.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Publisher.kt new file mode 100644 index 00000000..e0bd1353 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Publisher.kt @@ -0,0 +1,188 @@ +// +// 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.publication + +import io.zenoh.* +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.value.Value +import kotlin.Throws + +/** + * A Zenoh Publisher. + * + * A publisher is automatically dropped when using it with the 'try-with-resources' statement (i.e. 'use' in Kotlin). + * The session from which it was declared will also keep a reference to it and undeclare it once the session is closed. + * + * In order to declare a publisher, [Session.declarePublisher] must be called, which returns a [Publisher.Builder] from + * which we can specify the [Priority], and the [CongestionControl]. + * + * Example: + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/java/greeting")) { + * System.out.println("Declaring publisher on '" + keyExpr + "'..."); + * try (Publisher publisher = session.declarePublisher(keyExpr).res()) { + * int i = 0; + * while (true) { + * publisher.put("Hello for the " + i + "th time!").res(); + * Thread.sleep(1000); + * i++; + * } + * } + * } + * } catch (ZenohException | InterruptedException e) { + * System.out.println("Error: " + e); + * } + * ``` + * + * The publisher configuration parameters can be later changed using the setter functions. + * + * @property keyExpr The key expression the publisher will be associated to. + * @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 jniPublisher: JNIPublisher?, + private var congestionControl: CongestionControl, + private var priority: Priority +) : SessionDeclaration, AutoCloseable { + + companion object { + private val sessionException = SessionException("Publisher is not valid.") + } + + /** Performs a PUT operation on the specified [keyExpr] with the specified [value]. */ + @Throws(ZenohException::class) + fun put(value: Value): Resolvable = Resolvable { + return@Resolvable jniPublisher?.put(value) ?: throw(sessionException) + } + + /** Performs a PUT operation on the specified [keyExpr] with the specified string [value]. */ + @Throws(ZenohException::class) + fun put(value: String): Resolvable = Resolvable { + return@Resolvable jniPublisher?.put(Value(value)) ?: throw(sessionException) + } + + /** + * Performs a WRITE operation on the specified [keyExpr] + * + * @param kind The [SampleKind] of the data. + * @param value The [Value] to send. + * @return A [Resolvable] operation. + */ + @Throws(ZenohException::class) + fun write(kind: SampleKind, value: Value): Resolvable = Resolvable { + return@Resolvable jniPublisher?.write(kind, value) ?: throw(sessionException) + } + + /** + * Performs a DELETE operation on the specified [keyExpr] + * + * @return A [Resolvable] operation. + */ + @Throws(ZenohException::class) + fun delete(): Resolvable = Resolvable { + return@Resolvable jniPublisher?.delete() ?: throw(sessionException) + } + + /** 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 + } + + /** 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 + } + + override fun isValid(): Boolean { + return jniPublisher != null + } + + override fun close() { + undeclare() + } + + override fun undeclare() { + jniPublisher?.close() + jniPublisher = null + } + + protected fun finalize() { + jniPublisher?.close() + } + + /** + * Publisher Builder. + * + * @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, + ) { + + /** Change the `congestion_control` to apply when routing the data. */ + fun congestionControl(congestionControl: CongestionControl) = + apply { this.congestionControl = congestionControl } + + /** Change the priority of the written data. */ + fun priority(priority: Priority) = apply { this.priority = priority } + + fun res(): Publisher { + return session.run { resolvePublisher(this@Builder) } + } + } +} + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Put.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Put.kt new file mode 100644 index 00000000..132a7025 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/publication/Put.kt @@ -0,0 +1,119 @@ +// +// 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.publication + +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.value.Value + +/** + * Put operation. + * + * A put puts a [io.zenoh.sample.Sample] into the specified key expression. + * + * Example: + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/zenoh-java-put")) { + * String value = "Put from Java!"; + * session.put(keyExpr, value) + * .congestionControl(CongestionControl.BLOCK) + * .priority(Priority.REALTIME) + * .kind(SampleKind.PUT) + * .res(); + * System.out.println("Putting Data ('" + keyExpr + "': '" + value + "')..."); + * } + * } + * ``` + * + * This class is an open class for the sake of the [Delete] operation, which is a special case of [Put] operation. + * + * @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). + */ +open class Put protected constructor( + val keyExpr: KeyExpr, + val value: Value, + val congestionControl: CongestionControl, + val priority: Priority, + val kind: SampleKind +) { + + companion object { + + /** + * Creates a bew [Builder] associated to the specified [session] and [keyExpr]. + * + * @param session The [Session] from which the put will be performed. + * @param keyExpr The [KeyExpr] upon which the put will be performed. + * @param value The [Value] to put. + * @return A [Put] operation [Builder]. + */ + fun newBuilder(session: Session, keyExpr: KeyExpr, value: Value): Builder { + return Builder(session, keyExpr, value) + } + } + + /** + * Builder to construct a [Put] operation. + * + * @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 + ): Resolvable { + + /** Change the [Encoding] of the written data. */ + fun encoding(encoding: Encoding) = apply { + this.value = Value(value.payload, encoding) + } + + /** Change the [CongestionControl] to apply when routing the data. */ + fun congestionControl(congestionControl: CongestionControl) = + apply { this.congestionControl = congestionControl } + + /** Change the [Priority] of the written data. */ + fun priority(priority: Priority) = apply { this.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 } + + /** Resolves the put operation. */ + @Throws(ZenohException::class) + override fun res() { + val put = Put(keyExpr, value, congestionControl, priority, kind) + 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 new file mode 100644 index 00000000..e430358a --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/ConsolidationMode.kt @@ -0,0 +1,35 @@ +// +// 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.query + +/** The kind of consolidation. */ +enum class ConsolidationMode { + /** No consolidation applied: multiple samples may be received for the same key-timestamp.*/ + NONE, + + /** + * Monotonic consolidation immediately forwards samples, except if one with an equal or more recent timestamp + * has already been sent with the same key. + * + * This optimizes latency while potentially reducing bandwidth. + * + * Note that this doesn't cause re-ordering, but drops the samples for which a more recent timestamp has already + * been observed with the same key. + */ + MONOTONIC, + + /** Holds back samples to only send the set of samples that had the highest timestamp for their key. */ + LATEST; +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Get.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Get.kt new file mode 100644 index 00000000..7b1070e0 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Get.kt @@ -0,0 +1,185 @@ +// +// 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.query + +import io.zenoh.handlers.Callback +import io.zenoh.Session +import io.zenoh.exceptions.ZenohException +import io.zenoh.handlers.BlockingQueueHandler +import io.zenoh.handlers.Handler +import io.zenoh.selector.Selector +import io.zenoh.value.Value +import java.time.Duration +import java.util.* +import java.util.concurrent.BlockingQueue +import java.util.concurrent.LinkedBlockingDeque + +/** + * Get to query data from the matching queryables in the system. + * + * Example with a [Callback]: + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/java/example")) { + * session.get(keyExpr) + * .consolidation(ConsolidationMode.NONE) + * .withValue("Get value example") + * .with(reply -> System.out.println("Received reply " + reply)) + * .res() + * } + * } + * ``` + * + * @param R Receiver type of the [Handler] implementation. If no handler is provided to the builder, R will be [Unit]. + */ +class Get private constructor() { + + companion object { + /** + * Creates a bew [Builder] associated to the specified [session] and [keyExpr]. + * + * @param session The [Session] from which the query will be triggered. + * @param selector The [Selector] with which the query will be performed. + * @return A [Builder] with a default [BlockingQueueHandler] to handle any incoming [Reply]. + */ + fun newBuilder(session: Session, selector: Selector): Builder>> { + return Builder(session, selector, handler = BlockingQueueHandler(LinkedBlockingDeque())) + } + } + + /** + * Builder to construct a [Get]. + * + * Either a [Handler] or a [Callback] must be specified. Note neither of them are stackable and are mutually exclusive, + * meaning that it is not possible to specify multiple callbacks and/or handlers, the builder only considers the + * last one specified. + * + * @param R The receiver type of the [Handler] implementation, defaults to [Unit] when no handler is specified. + * @property session The [Session] from which the query will be performed. + * @property selector The [Selector] with which the get query will be performed. + * @constructor Creates a Builder. This constructor is internal and should not be called directly. Instead, this + * builder should be obtained through the [Session] after calling [Session.get]. + */ + class Builder internal constructor( + private val session: Session, + private val selector: Selector, + private var callback: Callback? = null, + private var handler: Handler? = null, + ) { + + private var timeout = Duration.ofMillis(10000) + private var target: QueryTarget = QueryTarget.BEST_MATCHING + private var consolidation: ConsolidationMode = ConsolidationMode.NONE + private var value: Value? = null + private var onClose: (() -> Unit)? = null + + private constructor(other: Builder<*>, handler: Handler?) : this(other.session, other.selector) { + this.handler = handler + copyParams(other) + } + + private constructor(other: Builder<*>, callback: Callback?) : this(other.session, other.selector) { + this.callback = callback + copyParams(other) + } + + private fun copyParams(other: Builder<*>) { + this.timeout = other.timeout + this.target = other.target + this.consolidation = other.consolidation + this.value = other.value + this.onClose = other.onClose + } + + /** Specify the [QueryTarget]. */ + fun target(target: QueryTarget): Builder { + this.target = target + return this + } + + /** Specify the [ConsolidationMode]. */ + fun consolidation(consolidation: ConsolidationMode): Builder { + this.consolidation = consolidation + return this + } + + /** Specify the timeout. */ + fun timeout(timeout: Duration): Builder { + this.timeout = timeout + return this + } + + /** + * Specify a string value. A [Value] is generated with the provided message, therefore + * this method is equivalent to calling `withValue(Value(message))`. + */ + fun withValue(message: String): Builder { + this.value = Value(message) + return this + } + + /** Specify a [Value]. */ + fun withValue(value: Value): Builder { + this.value = value + return this + } + + /** + * Specify an action to be invoked when the Get operation is over. + * + * Zenoh will trigger ths specified action once no more replies are to be expected. + */ + fun onClose(action: () -> Unit): Builder { + this.onClose = action + return this + } + + /** Specify a [Callback]. Overrides any previously specified callback or handler. */ + fun with(callback: Callback): Builder = Builder(this, callback) + + /** Specify a [Handler]. Overrides any previously specified callback or handler. */ + fun with(handler: Handler): Builder = Builder(this, handler) + + /** Specify a [BlockingQueue]. Overrides any previously specified callback or handler. */ + fun with(blockingQueue: BlockingQueue>): Builder>> = Builder(this, BlockingQueueHandler(blockingQueue)) + + /** + * Resolve the builder triggering the query. + * + * @return The receiver [R] from the specified [Handler] (if specified). + */ + @Throws(ZenohException::class) + fun res(): R? { + require(callback != null || handler != null) { "Either a callback or a handler must be provided." } + val resolvedCallback = callback ?: Callback { t: Reply -> handler?.handle(t) } + val resolvedOnClose = fun() { + onClose?.invoke() + handler?.onClose() + } + return session.run { + resolveGet( + selector, + resolvedCallback, + resolvedOnClose, + handler?.receiver(), + timeout, + target, + consolidation, + value + ) + } + } + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/query/QueryTarget.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/QueryTarget.kt new file mode 100644 index 00000000..2711f6f6 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/QueryTarget.kt @@ -0,0 +1,35 @@ +// +// 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.query + +/** The Queryables that should be targeted by a GET operation. */ +enum class QueryTarget { + + /** + * Best Matching: the nearest complete queryable if any else all matching queryables. + */ + BEST_MATCHING, + + /** + * All matching queryables. + */ + ALL, + + /** + * All Complete queryables. + */ + ALL_COMPLETE; +} + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Reply.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Reply.kt new file mode 100644 index 00000000..e7b5ee0e --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/query/Reply.kt @@ -0,0 +1,191 @@ +// +// 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.query + +import io.zenoh.Resolvable +import io.zenoh.ZenohType +import io.zenoh.exceptions.ZenohException +import io.zenoh.sample.Sample +import io.zenoh.prelude.SampleKind +import io.zenoh.value.Value +import io.zenoh.keyexpr.KeyExpr +import io.zenoh.queryable.Query +import org.apache.commons.net.ntp.TimeStamp + +/** + * Class to represent a Zenoh Reply to a [Get] operation and to a remote [Query]. + * + * A reply can be either successful ([Success]) or an error ([Error]), both having different information. For instance, + * the successful reply will contain a [Sample] while the error reply will only contain a [Value] with the error information. + * + * Replies can either be automatically created when receiving a remote reply after performing a [Get] (in which case the + * [replierId] shows the id of the replier) or created through the builders while answering to a remote [Query] (in that + * case the replier ID is automatically added by Zenoh). + * + * Generating a reply only makes sense within the context of a [Query], therefore builders below are meant to only + * be accessible from [Query.reply]. + * + * Example: + * ```java + * session.declareQueryable(keyExpr).with { query -> + * query.reply(keyExpr) + * .success(Value("Hello")) + * .withTimeStamp(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 { + + /** + * 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.** + * + * @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. + * @constructor Create empty Builder + */ + class Builder internal constructor(val query: Query, val keyExpr: KeyExpr) { + + /** + * Returns a [Success.Builder] with the provided [value]. + * + * @param value The [Value] of the reply. + */ + fun success(value: Value) = Success.Builder(query, keyExpr, value) + + /** + * Returns a [Success.Builder] with a [Value] containing the provided [message]. + * + * It is equivalent to calling `success(Value(message))`. + * + * @param message A string message for the reply. + */ + fun success(message: String) = success(Value(message)) + +// TODO: uncomment line below when Zenoh enables Error replies. +// fun error(value: Value) = Error.Builder(query, value) + } + + /** + * A successful [Reply]. + * + * @property sample The [Sample] of the reply. + * @constructor Internal constructor, since replies are only meant to be generated upon receiving a remote reply + * or by calling [Query.reply] to reply to the specified [Query]. + * + * @param replierId The replierId of the remotely generated reply. + */ + class Success internal constructor(replierId: String, val sample: Sample) : Reply(replierId) { + + /** + * Builder for the [Success] reply. + * + * @property query The [Query] to reply to. + * @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 { + + private var kind = SampleKind.PUT + private var timeStamp: TimeStamp? = null + + /** + * Sets the [SampleKind] of the replied [Sample]. + */ + fun withKind(kind: SampleKind) = apply { this.kind = kind } + + /** + * Sets the [TimeStamp] of the replied [Sample]. + */ + fun withTimeStamp(timeStamp: TimeStamp) = apply { this.timeStamp = timeStamp } + + /** + * 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) + return query.reply(Success("", sample)).res() + } + } + + override fun toString(): String { + return "Success(sample=$sample)" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Success) return false + + return sample == other.sample + } + + override fun hashCode(): Int { + return sample.hashCode() + } + } + + /** + * An Error reply. + * + * @property error: value with the error information. + * @constructor The constructor is private since reply instances are created through JNI when receiving a reply to a query. + * + * @param replierId: unique ID identifying the replier. + */ + class Error internal constructor(replierId: String, val error: Value) : Reply(replierId) { + + /** + * Builder for the [Error] reply. + * + * @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 { + + /** + * Triggers the error reply. + */ + override fun res() { + return query.reply(Error("", value)).res() + } + } + + override fun toString(): String { + return "Error(error=$error)" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is Error) return false + + return error == other.error + } + + override fun hashCode(): Int { + return error.hashCode() + } + } +} + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Query.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Query.kt new file mode 100644 index 00000000..faa27977 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Query.kt @@ -0,0 +1,86 @@ +// +// 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.queryable + +import io.zenoh.Resolvable +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 + +/** + * Represents a Zenoh Query in Kotlin. + * + * A Query is generated within the context of a [Queryable], when receiving a [Query] request. + * + * @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 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. + */ +class Query internal constructor( + val keyExpr: KeyExpr, + val selector: Selector, + val value: Value?, + private var jniQuery: JNIQuery? +) : AutoCloseable, ZenohType { + + /** Shortcut to the [selector]'s parameters. */ + val parameters = selector.parameters + + /** + * Reply to the specified key expression. + * + * @param keyExpr Key expression to reply to. This parameter must not be necessarily the same + * as the key expression from the Query, however it must intersect with the query key. + * @return a [Reply.Builder] + */ + fun reply(keyExpr: KeyExpr) = Reply.Builder(this, keyExpr) + + override fun close() { + jniQuery?.apply { + this.close() + jniQuery = null + } + } + + protected fun finalize() { + close() + } + + /** + * Perform a reply operation to the remote [Query]. + * + * @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. + */ + @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() + jniQuery = null + return@Resolvable result + } + throw(SessionException("Query is invalid")) + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Queryable.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Queryable.kt new file mode 100644 index 00000000..1c13f44d --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/queryable/Queryable.kt @@ -0,0 +1,176 @@ +// +// 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.queryable + +import io.zenoh.* +import io.zenoh.exceptions.ZenohException +import io.zenoh.handlers.Callback +import io.zenoh.handlers.BlockingQueueHandler +import io.zenoh.handlers.Handler +import io.zenoh.jni.JNIQueryable +import io.zenoh.keyexpr.KeyExpr +import java.util.* +import java.util.concurrent.BlockingQueue +import java.util.concurrent.LinkedBlockingDeque + +/** + * A queryable that allows to perform multiple queries on the specified [KeyExpr]. + * + * Its main purpose is to keep the queryable active as long as it exists. + * + * Example using the default [BlockingQueueHandler] handler: + * ```java + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example/zenoh-java-queryable")) { + * System.out.println("Declaring Queryable"); + * try (Queryable>> queryable = session.declareQueryable(keyExpr).res()) { + * BlockingQueue> receiver = queryable.getReceiver(); + * while (true) { + * Optional wrapper = receiver.take(); + * if (wrapper.isEmpty()) { + * break; + * } + * Query query = wrapper.get(); + * 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(); + * } catch (Exception e) { + * System.out.println(">> [Queryable] Error sending reply: " + e); + * } + * } + * } + * } + * } + * ``` + * + * @param R Receiver type of the [Handler] implementation. If no handler is provided to the builder, [R] will be [Unit]. + * @property keyExpr The [KeyExpr] to which the subscriber is associated. + * @property receiver Optional [R] that is provided when specifying a [Handler] for the subscriber. + * @property jniQueryable Delegate object in charge of communicating with the underlying native code. + * @constructor Internal constructor. Instances of Queryable must be created through the [Builder] obtained after + * calling [Session.declareQueryable] or alternatively through [newBuilder]. + */ +class Queryable internal constructor( + val keyExpr: KeyExpr, val receiver: R?, private var jniQueryable: JNIQueryable? +) : AutoCloseable, SessionDeclaration { + + override fun isValid(): Boolean { + return jniQueryable != null + } + + override fun undeclare() { + jniQueryable?.close() + jniQueryable = null + } + + override fun close() { + undeclare() + } + + protected fun finalize() { + jniQueryable?.close() + } + + companion object { + + /** + * Creates a new [Builder] associated to the specified [session] and [keyExpr]. + * + * @param session The [Session] from which the queryable will be declared. + * @param keyExpr The [KeyExpr] associated to the queryable. + * @return An empty [Builder] with a default [BlockingQueueHandler] to handle the incoming samples. + */ + fun newBuilder(session: Session, keyExpr: KeyExpr): Builder>> { + return Builder(session, keyExpr, handler = BlockingQueueHandler(queue = LinkedBlockingDeque())) + } + } + + /** + * Builder to construct a [Queryable]. + * + * Either a [Handler] or a [Callback] must be specified. Note neither of them are stackable and are mutually exclusive, + * meaning that it is not possible to specify multiple callbacks and/or handlers, the builder only considers the + * last one specified. + * + * @param R Receiver type of the [Handler] implementation. If no handler is provided to the builder, R will be [Unit]. + * @property session [Session] to which the [Queryable] will be bound to. + * @property keyExpr The [KeyExpr] to which the queryable is associated. + * @property callback Optional callback that will be triggered upon receiving a [Query]. + * @property handler Optional handler to receive the incoming queries. + * @constructor Creates a Builder. This constructor is internal and should not be called directly. Instead, this + * builder should be obtained through the [Session] after calling [Session.declareQueryable]. + */ + class Builder internal constructor( + private val session: Session, + private val keyExpr: KeyExpr, + private var callback: Callback? = null, + private var handler: Handler? = null + ): Resolvable> { + private var complete: Boolean = false + private var onClose: (() -> Unit)? = null + + private constructor(other: Builder<*>, handler: Handler?) : this(other.session, other.keyExpr) { + this.handler = handler + this.complete = other.complete + this.onClose = other.onClose + } + + private constructor(other: Builder<*>, callback: Callback?) : this(other.session, other.keyExpr) { + this.callback = callback + this.complete = other.complete + this.onClose = other.onClose + } + + /** Change queryable completeness. */ + fun complete(complete: Boolean) = apply { this.complete = complete } + + /** Specify an action to be invoked when the [Queryable] is undeclared. */ + fun onClose(action: () -> Unit): Builder { + this.onClose = action + return this + } + + /** Specify a [Callback]. Overrides any previously specified callback or handler. */ + fun with(callback: Callback): Builder = Builder(this, callback) + + /** Specify a [Handler]. Overrides any previously specified callback or handler. */ + fun with(handler: Handler): Builder = Builder(this, handler) + + /** Specify a [BlockingQueue]. Overrides any previously specified callback or handler. */ + fun with(blockingQueue: BlockingQueue>): Builder>> = Builder(this, BlockingQueueHandler(blockingQueue)) + + /** + * Resolve the builder, creating a [Queryable] with the provided parameters. + * + * @return The newly created [Queryable]. + */ + @Throws(ZenohException::class) + override fun res(): Queryable { + require(callback != null || handler != null) { "Either a callback or a handler must be provided." } + val resolvedCallback = callback ?: Callback { t: Query -> handler?.handle(t) } + val resolvedOnClose = fun() { + handler?.onClose() + onClose?.invoke() + } + return session.run { resolveQueryable(keyExpr, resolvedCallback, resolvedOnClose, handler?.receiver(), complete) } + } + } +} + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Sample.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Sample.kt new file mode 100644 index 00000000..e84cd96d --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/sample/Sample.kt @@ -0,0 +1,63 @@ +// +// 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 + +import io.zenoh.ZenohType +import io.zenoh.prelude.SampleKind +import io.zenoh.keyexpr.KeyExpr +import io.zenoh.value.Value +import org.apache.commons.net.ntp.TimeStamp + +/** + * Class representing a Zenoh Sample. + * + * A sample consists of a [KeyExpr]-[Value] pair, annotated with the [SampleKind] (PUT or DELETE) of the publication + * used to emit it and a timestamp. + * + * @property keyExpr The [KeyExpr] of the sample. + * @property value The [Value] of the sample. + * @property kind The [SampleKind] of the sample. + * @property timestamp Optional [TimeStamp]. + */ +class Sample( + val keyExpr: KeyExpr, + val value: Value, + val kind: SampleKind, + val timestamp: TimeStamp? +): ZenohType { + override fun toString(): String { + return if (kind == SampleKind.DELETE) "$kind($keyExpr)" else "$kind($keyExpr: $value)" + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Sample + + if (keyExpr != other.keyExpr) return false + if (value != other.value) return false + if (kind != other.kind) return false + return timestamp == other.timestamp + } + + override fun hashCode(): Int { + var result = keyExpr.hashCode() + result = 31 * result + value.hashCode() + result = 31 * result + kind.hashCode() + result = 31 * result + (timestamp?.hashCode() ?: 0) + return result + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/selector/IntoSelector.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/selector/IntoSelector.kt new file mode 100644 index 00000000..fe47ac68 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/selector/IntoSelector.kt @@ -0,0 +1,30 @@ +// +// 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.selector + +import io.zenoh.exceptions.KeyExprException +import io.zenoh.keyexpr.KeyExpr + +@Throws(KeyExprException::class) +fun String.intoSelector(): Selector { + if (this.isEmpty()) { + throw(KeyExprException("Attempting to create a KeyExpr from an empty string.")) + } + val result = this.split('?', limit = 2) + val keyExpr = KeyExpr.autocanonize(result[0]) + val params = if (result.size == 2) result[1] else "" + return Selector(keyExpr, params) +} + diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/selector/Selector.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/selector/Selector.kt new file mode 100644 index 00000000..133bcc04 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/selector/Selector.kt @@ -0,0 +1,65 @@ +// +// 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.selector + +import io.zenoh.exceptions.KeyExprException +import io.zenoh.keyexpr.KeyExpr + +/** + * A selector is the combination of a [KeyExpr], which defines the + * set of keys that are relevant to an operation, and a [parameters], a set of key-value pairs with a few uses: + * + * * specifying arguments to a queryable, allowing the passing of Remote Procedure Call parameters + * * filtering by value, + * * filtering by metadata, such as the timestamp of a value + * + * @property keyExpr The [KeyExpr] of the selector. + * @property parameters The parameters of the selector. + */ +class Selector(val keyExpr: KeyExpr, val parameters: String = ""): AutoCloseable { + + companion object { + + /** + * Try from. + * + * Equivalent constructor to [String.intoSelector], generates a selector from a string. + * + * @param expression A string with the form "?". + * @return A [Selector] in case of success. + * @throws KeyExprException in case of failure generating the key expression. + */ + @JvmStatic + @Throws(KeyExprException::class) + fun tryFrom(expression: String): Selector { + if (expression.isEmpty()) { + throw(KeyExprException("Attempting to create a KeyExpr from an empty string.")) + } + val result = expression.split('?', limit = 2) + val keyExpr = KeyExpr.autocanonize(result[0]) + val params = if (result.size == 2) result[1] else "" + return Selector(keyExpr, params) + } + } + + override fun toString(): String { + return if (parameters.isEmpty()) "$keyExpr" else "$keyExpr?$parameters" + } + + /** Closes the selector's [KeyExpr]. */ + override fun close() { + keyExpr.close() + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/subscriber/Reliability.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/subscriber/Reliability.kt new file mode 100644 index 00000000..bc47f36b --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/subscriber/Reliability.kt @@ -0,0 +1,39 @@ +// +// 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.subscriber + +/** + * The reliability policy. + * + * Used by subscribers to inform the network of the reliability it wishes to obtain. + */ +enum class Reliability { + /** + * Best Effort + * + * Informs the network that dropping some messages is acceptable. + */ + BEST_EFFORT, + + /** + * Reliable + * + * 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, + * 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/subscriber/Subscriber.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/subscriber/Subscriber.kt new file mode 100644 index 00000000..53b41f3a --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/subscriber/Subscriber.kt @@ -0,0 +1,184 @@ +// +// 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.subscriber + +import io.zenoh.* +import io.zenoh.exceptions.ZenohException +import io.zenoh.handlers.Callback +import io.zenoh.handlers.BlockingQueueHandler +import io.zenoh.handlers.Handler +import io.zenoh.subscriber.Subscriber.Builder +import io.zenoh.jni.JNISubscriber +import io.zenoh.keyexpr.KeyExpr +import io.zenoh.sample.Sample +import java.util.* +import java.util.concurrent.BlockingQueue +import java.util.concurrent.LinkedBlockingDeque + +/** + * A subscriber that allows listening to updates on a key expression and reacting to changes. + * + * Its main purpose is to keep the subscription active as long as it exists. + * + * Example using the default [BlockingQueueHandler] handler: + * + * ```java + * System.out.println("Opening session..."); + * try (Session session = Session.open()) { + * try (KeyExpr keyExpr = KeyExpr.tryFrom("demo/example")) { + * System.out.println("Declaring Subscriber on '" + keyExpr + "'..."); + * try (Subscriber>> subscriber = session.declareSubscriber(keyExpr).res()) { + * BlockingQueue> receiver = subscriber.getReceiver(); + * assert receiver != null; + * while (true) { + * Optional wrapper = receiver.take(); + * if (wrapper.isEmpty()) { + * break; + * } + * Sample sample = wrapper.get(); + * System.out.println(">> [Subscriber] Received " + sample.getKind() + " ('" + sample.getKeyExpr() + "': '" + sample.getValue() + "')"); + * } + * } + * } + * } + * ``` + * + * @param R Receiver type of the [Handler] implementation. If no handler is provided to the builder, R will be [Unit]. + * @property keyExpr The [KeyExpr] to which the subscriber is associated. + * @property receiver Optional [R] that is provided when specifying a [Handler] for the subscriber. + * @property jniSubscriber Delegate object in charge of communicating with the underlying native code. + * @constructor Internal constructor. Instances of Subscriber must be created through the [Builder] obtained after + * calling [Session.declareSubscriber] or alternatively through [newBuilder]. + */ +class Subscriber internal constructor( + val keyExpr: KeyExpr, val receiver: R?, private var jniSubscriber: JNISubscriber? +) : AutoCloseable, SessionDeclaration { + + override fun isValid(): Boolean { + return jniSubscriber != null + } + + override fun undeclare() { + jniSubscriber?.close() + jniSubscriber = null + } + + override fun close() { + undeclare() + } + + protected fun finalize() { + jniSubscriber?.close() + } + + companion object { + + /** + * Creates a new [Builder] associated to the specified [session] and [keyExpr]. + * + * @param session The [Session] from which the subscriber will be declared. + * @param keyExpr The [KeyExpr] associated to the subscriber. + * @return An empty [Builder] with a default [BlockingQueueHandler] to handle the incoming samples. + */ + fun newBuilder(session: Session, keyExpr: KeyExpr): Builder>> { + return Builder(session, keyExpr, handler = BlockingQueueHandler(queue = LinkedBlockingDeque())) + } + } + + /** + * Builder to construct a [Subscriber]. + * + * Either a [Handler] or a [Callback] must be specified. Note neither of them are stackable and are mutually exclusive, + * meaning that it is not possible to specify multiple callbacks and/or handlers, the builder only considers the + * last one specified. + * + * @param R Receiver type of the [Handler] implementation. If no handler is provided to the builder, R will be [Unit]. + * @property session [Session] to which the [Subscriber] will be bound to. + * @property keyExpr The [KeyExpr] to which the subscriber is associated. + * @constructor Creates a Builder. This constructor is internal and should not be called directly. Instead, this + * builder should be obtained through the [Session] after calling [Session.declareSubscriber]. + */ + class Builder internal constructor( + private val session: Session, + private val keyExpr: KeyExpr, + private var callback: Callback? = null, + private var handler: Handler? = null + ): Resolvable> { + + private var reliability: Reliability = Reliability.BEST_EFFORT + private var onClose: (() -> Unit)? = null + + private constructor(other: Builder<*>, handler: Handler?): this(other.session, other.keyExpr) { + this.handler = handler + copyParams(other) + } + + private constructor(other: Builder<*>, callback: Callback?) : this(other.session, other.keyExpr) { + this.callback = callback + copyParams(other) + } + + private fun copyParams(other: Builder<*>) { + this.reliability = other.reliability + this.onClose = other.onClose + } + + /** Sets the [Reliability]. */ + fun reliability(reliability: Reliability): Builder = apply { + this.reliability = reliability + } + + /** Sets the reliability to [Reliability.RELIABLE]. */ + fun reliable(): Builder = apply { + this.reliability = Reliability.RELIABLE + } + + /** Sets the reliability to [Reliability.BEST_EFFORT]. */ + fun bestEffort(): Builder = apply { + this.reliability = Reliability.BEST_EFFORT + } + + /** Specify an action to be invoked when the [Subscriber] is undeclared. */ + fun onClose(action: () -> Unit): Builder { + this.onClose = action + return this + } + + /** Specify a [Callback]. Overrides any previously specified callback or handler. */ + fun with(callback: Callback): Builder = Builder(this, callback) + + /** Specify a [Handler]. Overrides any previously specified callback or handler. */ + fun with(handler: Handler): Builder = Builder(this, handler) + + /** Specify a [BlockingQueue]. Overrides any previously specified callback or handler. */ + fun with(blockingQueue: BlockingQueue>): Builder>> = Builder(this, BlockingQueueHandler(blockingQueue)) + + /** + * Resolve the builder, creating a [Subscriber] with the provided parameters. + * + * @return The newly created [Subscriber]. + */ + @Throws(ZenohException::class) + override fun res(): Subscriber { + require(callback != null || handler != null) { "Either a callback or a handler must be provided." } + val resolvedCallback = callback ?: Callback { t: Sample -> handler?.handle(t) } + val resolvedOnClose = fun() { + handler?.onClose() + onClose?.invoke() + } + return session.run { resolveSubscriber(keyExpr, resolvedCallback, resolvedOnClose, handler?.receiver(), reliability) } + } + } +} diff --git a/zenoh-java/src/commonMain/kotlin/io/zenoh/value/Value.kt b/zenoh-java/src/commonMain/kotlin/io/zenoh/value/Value.kt new file mode 100644 index 00000000..d88c1272 --- /dev/null +++ b/zenoh-java/src/commonMain/kotlin/io/zenoh/value/Value.kt @@ -0,0 +1,67 @@ +// +// 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.value + +import io.zenoh.prelude.Encoding +import io.zenoh.prelude.KnownEncoding + +/** + * A Zenoh value. + * + * A Value is a pair of a binary payload, and a mime-type-like encoding string. + * + * @property payload The payload of this Value. + * @property encoding An encoding description indicating how the associated payload is encoded. + */ +class Value(val payload: ByteArray, val encoding: Encoding) { + + /** + * Constructs a value with the provided message, using [KnownEncoding.TEXT_PLAIN] for encoding. + */ + constructor(message: String): this(message.toByteArray(), Encoding(KnownEncoding.TEXT_PLAIN)) + + /** + * Constructs a value with the provided message and encoding. + */ + constructor(message: String, encoding: Encoding): this(message.toByteArray(), encoding) + + companion object { + + /** Return an empty value. */ + fun empty(): Value { + return Value(ByteArray(0), Encoding(KnownEncoding.APP_OCTET_STREAM)) + } + } + + override fun toString(): String { + return payload.decodeToString() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Value + + if (!payload.contentEquals(other.payload)) return false + return encoding == other.encoding + } + + override fun hashCode(): Int { + var result = payload.contentHashCode() + result = 31 * result + encoding.hashCode() + return result + } +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/ConfigTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/ConfigTest.kt new file mode 100644 index 00000000..adaaf9f2 --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/ConfigTest.kt @@ -0,0 +1,75 @@ +// +// 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.keyexpr.intoKeyExpr +import io.zenoh.sample.Sample +import io.zenoh.value.Value +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class ConfigTest { + companion object { + val TEST_KEY_EXP = "example/testing/keyexpr".intoKeyExpr() + } + + @Test + fun configLoadsJsonTest() { + // There are a bunch of different possible configurations. + // For this test we do the following: + // - Modifying the default endpoints. + // - Disabling multicast scouting to avoid unintended connections. + val clientCfg = "{\n" + + " \"mode\": \"peer\",\n" + + " \"connect\": {\n" + + " \"endpoints\": [\"tcp/localhost:7450\"]\n" + + " },\n" + + " \"scouting\": {\n" + + " \"multicast\": {\n" + + " \"enabled\": false\n" + + " }\n" + + " }\n" + + "}\n" + + val serverCfg = "{\n" + + " \"mode\": \"peer\",\n" + + " \"listen\": {\n" + + " \"endpoints\": [\"tcp/localhost:7450\"]\n" + + " },\n" + + " \"scouting\": {\n" + + " \"multicast\": {\n" + + " \"enabled\": false\n" + + " }\n" + + " }\n" + + "}\n" + + val sessionClient = Session.open(Config.from(clientCfg)) + val sessionServer = Session.open(Config.from(serverCfg)) + var receivedSample: Sample? = null + val subscriber = sessionClient.declareSubscriber(TEST_KEY_EXP).with { sample -> receivedSample = sample }.res() + + val value = Value("encrypted_message") + sessionServer.put(TEST_KEY_EXP, value).res() + Thread.sleep(1000) + + subscriber.close() + sessionClient.close() + sessionServer.close() + + assertNotNull(receivedSample) + assertEquals(receivedSample!!.value, value) + } +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/DeleteTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/DeleteTest.kt new file mode 100644 index 00000000..22d3ae91 --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/DeleteTest.kt @@ -0,0 +1,42 @@ +// +// 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.keyexpr.intoKeyExpr +import io.zenoh.prelude.SampleKind +import io.zenoh.sample.Sample +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class DeleteTest { + + companion object { + val TEST_KEY_EXP = "example/testing/keyexpr".intoKeyExpr() + } + + @Test + fun subscriber_receivesDelete() { + val session = Session.open() + var receivedSample: Sample? = null + val subscriber = session.declareSubscriber(TEST_KEY_EXP).with { sample -> receivedSample = sample }.res() + session.delete(TEST_KEY_EXP).res() + subscriber.undeclare() + session.close() + + assertNotNull(receivedSample) + assertEquals(receivedSample!!.kind, SampleKind.DELETE) + } +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/GetTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/GetTest.kt new file mode 100644 index 00000000..79b94001 --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/GetTest.kt @@ -0,0 +1,216 @@ +// +// 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.handlers.Handler +import io.zenoh.keyexpr.KeyExpr +import io.zenoh.keyexpr.intoKeyExpr +import io.zenoh.prelude.SampleKind +import io.zenoh.query.Reply +import io.zenoh.queryable.Queryable +import io.zenoh.sample.Sample +import io.zenoh.selector.Selector +import io.zenoh.value.Value +import org.apache.commons.net.ntp.TimeStamp +import java.time.Duration +import java.util.* +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.Test + +class GetTest { + + companion object { + const val TEST_KEY_EXP = "example/testing/keyexpr" + const val TEST_KEY_EXP_WILD = "example/testing/*" + const val TEST_PAYLOAD = "Hello" + } + + @Test + fun get_runsWithCallback() { + val sessionA = Session.open() + + val value = Value(TEST_PAYLOAD) + val timeStamp = TimeStamp.getCurrentTime() + val kind = SampleKind.PUT + val keyExpr = TEST_KEY_EXP.intoKeyExpr() + val queryable = sessionA.declareQueryable(keyExpr).with { query -> + query.reply(keyExpr) + .success(value) + .withTimeStamp(timeStamp) + .withKind(kind) + .res() + }.res() + + val sessionB = Session.open() + + sessionB.get(keyExpr).with { reply: Reply -> + assertTrue(reply is Reply.Success) + assertEquals(value, reply.sample.value) + assertEquals(kind, reply.sample.kind) + assertEquals(keyExpr, reply.sample.keyExpr) + assertEquals(timeStamp, reply.sample.timestamp) + }.timeout(Duration.ofMillis(1000)).res() + + Thread.sleep(1000) + + queryable.undeclare() + sessionA.close() + sessionB.close() + } + + @Test + fun getWithSelectorParamsTest() { + val session = Session.open() + + var receivedParams = "" + val keyExpr = TEST_KEY_EXP.intoKeyExpr() + val queryable = session.declareQueryable(keyExpr).with { it.use { query -> + receivedParams = query.parameters + }}.res() + + val params = "arg1=val1,arg2=val2" + val selector = Selector(keyExpr, params) + session.get(selector).res() + + queryable.close() + session.close() + + assertEquals(params, receivedParams) + } + + @Test + fun get_runsWithHandler() { + val sessionA = Session.open() + val repliedSamples: ArrayList = ArrayList() + val queryablesAmount = 3 + val declaredQueryables: ArrayList> = ArrayList() + + val value = Value(TEST_PAYLOAD) + val timestamp = TimeStamp.getCurrentTime() + val kind = SampleKind.PUT + + for (i in 1..queryablesAmount) { + val keyExpr = KeyExpr.tryFrom(TEST_KEY_EXP + i.toString()) + val queryable = sessionA.declareQueryable(keyExpr).with { it.use { query -> + query.reply(keyExpr) + .success(value) + .withTimeStamp(timestamp) + .withKind(kind) + .res() + } + } + .res() + + declaredQueryables.add(queryable) + repliedSamples.add(Sample(keyExpr, value, kind, timestamp)) + } + + val sessionB = Session.open() + val receiver: ArrayList = + sessionB.get(TEST_KEY_EXP_WILD.intoKeyExpr()) + .with(GetHandler()) + .timeout(Duration.ofMillis(1000)) + .res()!! + + Thread.sleep(1000) + declaredQueryables.forEach { queryable -> queryable.undeclare() } + sessionA.close() + sessionB.close() + + assertEquals(queryablesAmount, receiver.size) + for (reply in receiver) { + reply as Reply.Success + val receivedSample = reply.sample + assertEquals(value, receivedSample.value) + assertEquals(SampleKind.PUT, receivedSample.kind) + assertEquals(timestamp, receivedSample.timestamp) + } + } + + @Test + fun get_runsWithBlockingQueue() { + val sessionA = Session.open() + + val queryablesAmount = 3 + val declaredQueryables: ArrayList> = ArrayList() + + val value = Value(TEST_PAYLOAD) + val timestamp = TimeStamp.getCurrentTime() + val kind = SampleKind.PUT + + for (i in 1..queryablesAmount) { + val keyExpr = (TEST_KEY_EXP + i.toString()).intoKeyExpr() + val queryable = sessionA.declareQueryable(keyExpr).with { it.use { query -> + query.reply(keyExpr) + .success(value) + .withTimeStamp(timestamp) + .withKind(kind) + .res() + } + } + .res() + + declaredQueryables.add(queryable) + } + + val receivedReplies = ArrayList(0) + + val sessionB = Session.open() + val receiver = sessionB.get(TEST_KEY_EXP_WILD.intoKeyExpr()).res()!! + + Thread.sleep(1000) + + val iterator = receiver.iterator() + while (iterator.hasNext()) { + try { + val reply = iterator.next().get() + receivedReplies.add(reply) + } catch (e: Exception) { + break + } + } + + sessionB.close() + + declaredQueryables.forEach { queryable -> queryable.undeclare() } + sessionA.close() + + assertEquals(queryablesAmount, receivedReplies.size) + for (reply in receivedReplies) { + assert(reply is Reply.Success) + val receivedSample = (reply as Reply.Success).sample + assertEquals(value, receivedSample.value) + assertEquals(SampleKind.PUT, receivedSample.kind) + assertEquals(timestamp, receivedSample.timestamp) + } + } +} + +/** A dummy handler for get operations. */ +private class GetHandler : Handler> { + + val performedReplies: ArrayList = ArrayList() + + override fun handle(t: Reply) { + performedReplies.add(t) + } + + override fun receiver(): ArrayList { + return performedReplies + } + + override fun onClose() {} +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt new file mode 100644 index 00000000..cbefc369 --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/KeyExprTest.kt @@ -0,0 +1,136 @@ +// +// 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.exceptions.SessionException +import io.zenoh.keyexpr.KeyExpr +import io.zenoh.keyexpr.intoKeyExpr +import org.junit.Assert.assertThrows +import kotlin.test.* + +class KeyExprTest { + + init { + Zenoh.load() + } + + @Test + fun creation_TryFromTest() { + // A couple of examples of valid and invalid key expressions. + + KeyExpr.tryFrom("example/test") // Should not throw exception + + assertThrows(Exception::class.java) { KeyExpr.tryFrom("example/test?param='test'") } + + KeyExpr.tryFrom("example/*/test") // Should not throw exception + + assertThrows(Exception::class.java) { KeyExpr.tryFrom("example/!*/test") } + } + + @Test + fun equalizationTest() { + val keyExpr1 = KeyExpr.tryFrom("example/test") + val keyExpr2 = KeyExpr.tryFrom("example/test") + + assertEquals(keyExpr1, keyExpr2) + + val keyExpr3 = KeyExpr.tryFrom("different/key/expr") + assertNotEquals(keyExpr1, keyExpr3) + + keyExpr2.close() + assertNotEquals(keyExpr1, keyExpr2) + + keyExpr1.close() + assertNotEquals(keyExpr1, keyExpr2) + } + + @Test + fun creation_autocanonizeTest() { + val keyExpr1 = KeyExpr.autocanonize("example/**/test") + val keyExpr2 = KeyExpr.autocanonize("example/**/**/test") + 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") + + val keyExprB = KeyExpr.tryFrom("example/B/test") + assertTrue(keyExprA.intersects(keyExprB)) + + val keyExprC = KeyExpr.tryFrom("example/B/C/test") + assertFalse(keyExprA.intersects(keyExprC)) + + val keyExprA2 = KeyExpr.tryFrom("example/**") + assertTrue(keyExprA2.intersects(keyExprC)) + } + + @Test + fun includesTest() { + val keyExpr = KeyExpr.tryFrom("example/**") + val includedKeyExpr = KeyExpr.tryFrom("example/A/B/C/D") + assertTrue(keyExpr.includes(includedKeyExpr)) + + val notIncludedKeyExpr = KeyExpr.tryFrom("C/D") + assertFalse(keyExpr.includes(notIncludedKeyExpr)) + } + + @Test + fun sessionDeclarationTest() { + val session = Session.open() + val keyExpr = session.declareKeyExpr("a/b/c").res() + assertEquals("a/b/c", keyExpr.toString()) + session.close() + keyExpr.close() + } + + @Test + fun sessionUnDeclarationTest() { + val session = Session.open() + val keyExpr = session.declareKeyExpr("a/b/c").res() + assertEquals("a/b/c", keyExpr.toString()) + + session.undeclare(keyExpr).res() // Should not throw exception + + // Undeclaring a key expr that was not declared through a session. + val keyExpr2 = "x/y/z".intoKeyExpr() + assertThrows(SessionException::class.java) {session.undeclare(keyExpr2).res()} + + session.close() + keyExpr.close() + keyExpr2.close() + } + + @Test + fun keyExprIsValidAfterClosingSession() { + val session = Session.open() + val keyExpr = session.declareKeyExpr("a/b/c").res() + session.close() + + assertTrue(keyExpr.isValid()) + assertFalse(keyExpr.toString().isEmpty()) // An operation such as toString that goes through JNI is still valid. + } +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/PublisherTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/PublisherTest.kt new file mode 100644 index 00000000..7fe63c8a --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/PublisherTest.kt @@ -0,0 +1,104 @@ +// +// 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.prelude.KnownEncoding +import io.zenoh.keyexpr.intoKeyExpr +import io.zenoh.prelude.Encoding +import io.zenoh.prelude.SampleKind +import io.zenoh.sample.Sample +import io.zenoh.value.Value +import kotlin.test.Test +import kotlin.test.assertEquals + +class PublisherTest { + + companion object { + val TEST_KEY_EXP = "example/testing/keyexpr".intoKeyExpr() + } + + @Test + fun putTest() { + val session = Session.open() + + val receivedSamples = ArrayList() + val publisher = session.declarePublisher(TEST_KEY_EXP).res() + val subscriber = session.declareSubscriber(TEST_KEY_EXP).with { sample -> + receivedSamples.add(sample) + }.res() + + 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)) + ) + + testValues.forEach() { value -> publisher.put(value).res() } + subscriber.undeclare() + publisher.undeclare() + session.close() + + assertEquals(receivedSamples.size, testValues.size) + for ((index, sample) in receivedSamples.withIndex()) { + assertEquals(sample.value, testValues[index]) + } + } + + @Test + fun writeTest() { + val session = Session.open() + val receivedSamples = ArrayList() + val subscriber = session.declareSubscriber(TEST_KEY_EXP).with { sample -> + receivedSamples.add(sample) + }.res() + + val testSamples = arrayListOf( + Sample(TEST_KEY_EXP, Value("Test PUT"), SampleKind.PUT, null), + Sample(TEST_KEY_EXP, Value("Test DELETE"), SampleKind.DELETE, null), + ) + + val publisher = session.declarePublisher(TEST_KEY_EXP).res() + publisher.write(testSamples[0].kind, testSamples[0].value).res() + publisher.write(testSamples[1].kind, testSamples[1].value).res() + + subscriber.undeclare() + publisher.undeclare() + session.close() + assertEquals(testSamples.size, receivedSamples.size) + for ((index, sample) in receivedSamples.withIndex()) { + assertEquals(sample, testSamples[index]) + } + } + + @Test + fun deleteTest() { + val session = Session.open() + + val receivedSamples = ArrayList() + val subscriber = session.declareSubscriber(TEST_KEY_EXP).with { sample -> + receivedSamples.add(sample) + }.res() + + val publisher = session.declarePublisher(TEST_KEY_EXP).res() + publisher.delete().res() + + publisher.undeclare() + subscriber.undeclare() + session.close() + + assertEquals(1, receivedSamples.size) + assertEquals(SampleKind.DELETE, receivedSamples[0].kind) + } +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/PutTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/PutTest.kt new file mode 100644 index 00000000..91b63f7a --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/PutTest.kt @@ -0,0 +1,46 @@ +// +// 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.prelude.KnownEncoding +import io.zenoh.keyexpr.intoKeyExpr +import io.zenoh.prelude.Encoding +import io.zenoh.sample.Sample +import io.zenoh.value.Value +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class PutTest { + + companion object { + const val TEST_KEY_EXP = "example/testing/keyexpr" + const val TEST_PAYLOAD = "Hello" + } + + @Test + fun subscriber_receivesPutValue() { + val session = Session.open() + 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)) + session.put(keyExpr, value).res() + subscriber.undeclare() + session.close() + assertNotNull(receivedSample) + assertEquals(value, receivedSample!!.value) + } +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/QueryableTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/QueryableTest.kt new file mode 100644 index 00000000..0cfb4f93 --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/QueryableTest.kt @@ -0,0 +1,170 @@ +// +// 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.handlers.Handler +import io.zenoh.keyexpr.intoKeyExpr +import io.zenoh.prelude.SampleKind +import io.zenoh.query.Reply +import io.zenoh.queryable.Query +import io.zenoh.sample.Sample +import io.zenoh.value.Value +import org.apache.commons.net.ntp.TimeStamp +import java.time.Duration +import java.time.Instant +import java.util.* +import java.util.concurrent.BlockingQueue +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class QueryableTest { + + companion object { + val TEST_KEY_EXP = "example/testing/keyexpr".intoKeyExpr() + const val TEST_PAYLOAD = "Hello queryable" + } + + /** Test validating both Queryable and get operations. */ + @Test + fun queryable_runsWithCallback() { + val sessionA = Session.open() + + val sample = Sample( + TEST_KEY_EXP, Value(TEST_PAYLOAD), SampleKind.PUT, TimeStamp(Date.from(Instant.now())) + ) + val queryable = sessionA.declareQueryable(TEST_KEY_EXP).with { query -> + query.reply(TEST_KEY_EXP).success(sample.value).withTimeStamp(sample.timestamp!!).res() + }.res() + + val sessionB = Session.open() + + sessionB.get(TEST_KEY_EXP).with { reply: Reply -> + assertTrue(reply is Reply.Success) + assertEquals(reply.sample, sample) + }.timeout(Duration.ofMillis(1000)).res() + + Thread.sleep(1000) + + queryable.undeclare() + sessionA.close() + sessionB.close() + } + + @Test + fun queryable_runsWithHandler() { + val sessionA = Session.open() + val handler = QueryHandler() + val queryable = sessionA.declareQueryable(TEST_KEY_EXP).with(handler).res() + + val sessionB = Session.open() + val receivedReplies = ArrayList() + sessionB.get(TEST_KEY_EXP).with { reply: Reply -> + receivedReplies.add(reply) + }.timeout(Duration.ofMillis(1000)).res() + + Thread.sleep(1000) + + queryable.undeclare() + sessionA.close() + sessionB.close() + + for (receivedReply in receivedReplies) { + assertTrue(receivedReply is Reply.Success) + } + assertEquals(handler.performedReplies, receivedReplies.map { reply -> (reply as Reply.Success).sample }) + } + + @Test + fun queryableBuilder_queueHandlerIsTheDefaultHandler() { + val session = Session.open() + val queryable = session.declareQueryable(TEST_KEY_EXP).res() + assertTrue(queryable.receiver is BlockingQueue>) + } + + @Test + fun queryTest() { + val session = Session.open() + var receivedQuery: Query? = null + val queryable = session.declareQueryable(TEST_KEY_EXP).with { query -> receivedQuery = query }.res() + + session.get(TEST_KEY_EXP).res() + + queryable.undeclare() + session.close() + + Thread.sleep(1000) + assertNotNull(receivedQuery) + assertNull(receivedQuery!!.value) + } + + @Test + fun queryWithValueTest() { + val session = Session.open() + var receivedQuery: Query? = null + val queryable = session.declareQueryable(TEST_KEY_EXP).with { query -> receivedQuery = query }.res() + + session.get(TEST_KEY_EXP).withValue("Test value").res() + + Thread.sleep(1000) + + queryable.undeclare() + session.close() + + assertNotNull(receivedQuery) + assertEquals(Value("Test value"), receivedQuery!!.value) + + } + + @Test + fun onCloseTest() { + val session = Session.open() + var onCloseWasCalled = false + val queryable = session.declareQueryable(TEST_KEY_EXP).onClose { onCloseWasCalled = true }.res() + queryable.undeclare() + assertTrue(onCloseWasCalled) + session.close() + } +} + +/** A dummy handler that replies "Hello queryable" followed by the count of replies performed. */ +private class QueryHandler : Handler { + + private var counter = 0 + + val performedReplies: ArrayList = ArrayList() + + override fun handle(t: Query) { + reply(t) + } + + override fun receiver(): QueryHandler { + return this + } + + override fun onClose() {} + + fun reply(query: Query) { + val payload = "Hello queryable $counter!" + counter++ + val sample = Sample( + query.keyExpr, Value(payload), SampleKind.PUT, TimeStamp(Date.from(Instant.now())) + ) + performedReplies.add(sample) + query.reply(query.keyExpr).success(sample.value).withTimeStamp(sample.timestamp!!).res() + } +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/SelectorTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/SelectorTest.kt new file mode 100644 index 00000000..99d133f4 --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/SelectorTest.kt @@ -0,0 +1,30 @@ +package io.zenoh + +import io.zenoh.exceptions.KeyExprException +import io.zenoh.selector.Selector +import io.zenoh.selector.intoSelector +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class SelectorTest { + + init { + Zenoh.load() + } + + @Test + fun selectorFromStringTest() { + "a/b/c?arg1=val1".intoSelector().use { selector: Selector -> + assertEquals("a/b/c", selector.keyExpr.toString()) + assertEquals("arg1=val1", selector.parameters) + } + + "a/b/c".intoSelector().use { selector: Selector -> + assertEquals("a/b/c", selector.keyExpr.toString()) + assertEquals("", selector.parameters) + } + + assertFailsWith { "".intoSelector() } + } +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/SessionTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/SessionTest.kt new file mode 100644 index 00000000..89d1a47b --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/SessionTest.kt @@ -0,0 +1,65 @@ +// +// 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.exceptions.SessionException +import io.zenoh.keyexpr.intoKeyExpr +import io.zenoh.sample.Sample +import kotlin.test.* + +class SessionTest { + + companion object { + val TEST_KEY_EXP = "example/testing/keyexpr".intoKeyExpr() + } + + @Test + fun sessionStartCloseTest() { + val session = Session.open() + assertTrue(session.isOpen()) + session.close() + assertFalse(session.isOpen()) + } + + @Test + fun sessionStop_stopUnopenedSessionIsNoOp() { + val session = Session.open() + session.close() + } + + @Test + fun sessionClose_doesNotThrowExceptionWhenStoppingSessionWithActiveDeclarations() { + val session = Session.open() + session.declarePublisher(TEST_KEY_EXP) + session.close() + } + + @Test + fun sessionDeclare_sessionIsOpenFromInitialization() { + val session = Session.open() + assertTrue(session.isOpen()) + session.close() + } + + @Test + fun sessionClose_newDeclarationsReturnNullAfterClosingSession() { + val session = Session.open() + session.close() + assertFailsWith { session.declarePublisher(TEST_KEY_EXP).res() } + assertFailsWith { session.declareSubscriber(TEST_KEY_EXP).with {}.res() } + assertFailsWith { session.declareQueryable(TEST_KEY_EXP).with {}.res() } + } + +} diff --git a/zenoh-java/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt b/zenoh-java/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt new file mode 100644 index 00000000..32bbf393 --- /dev/null +++ b/zenoh-java/src/commonTest/kotlin/io/zenoh/SubscriberTest.kt @@ -0,0 +1,114 @@ +// +// 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.handlers.Handler +import io.zenoh.prelude.KnownEncoding +import io.zenoh.keyexpr.intoKeyExpr +import io.zenoh.prelude.Encoding +import io.zenoh.sample.Sample +import io.zenoh.value.Value +import java.util.* +import java.util.concurrent.BlockingQueue +import kotlin.collections.ArrayDeque +import kotlin.collections.ArrayList +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.Test + +class SubscriberTest { + + companion object { + val TEST_KEY_EXP = "example/testing/keyexpr".intoKeyExpr() + + 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)) + ) + } + + @Test + fun subscriber_runsWithCallback() { + val session = Session.open() + val receivedSamples = ArrayList() + val subscriber = session.declareSubscriber(TEST_KEY_EXP).with { sample -> receivedSamples.add(sample) }.res() + + publishTestValues(session) + + subscriber.undeclare() + session.close() + + assertEquals(receivedSamples.size, testValues.size) + + for ((index, sample) in receivedSamples.withIndex()) { + assertEquals(sample.value, testValues[index]) + } + } + + @Test + fun subscriber_runsWithHandler() { + val handler = QueueHandler() + val session = Session.open() + val subscriber = session.declareSubscriber(TEST_KEY_EXP).with(handler).res() + publishTestValues(session) + subscriber.undeclare() + session.close() + + val queue = subscriber.receiver!! + assertEquals(queue.size, testValues.size) + for ((index, sample) in queue.withIndex()) { + assertEquals(sample.value, testValues[index]) + } + } + + @Test + fun subscriberBuilder_queueHandlerIsTheDefaultHandler() { + val session = Session.open() + val subscriber = session.declareSubscriber(TEST_KEY_EXP).res() + assertTrue(subscriber.receiver is BlockingQueue>) + } + + @Test + fun onCloseTest() { + val session = Session.open() + var onCloseWasCalled = false + val subscriber = session.declareSubscriber(TEST_KEY_EXP).onClose { onCloseWasCalled = true }.res() + subscriber.undeclare() + assertTrue(onCloseWasCalled) + session.close() + } + + private fun publishTestValues(session: Session): ArrayList { + val publisher = session.declarePublisher(TEST_KEY_EXP).res() + testValues.forEach { value -> publisher.put(value).res() } + publisher.undeclare() + return testValues + } +} + +private class QueueHandler : Handler> { + + val queue: ArrayDeque = ArrayDeque() + override fun handle(t: T) { + queue.add(t) + } + + override fun receiver(): ArrayDeque { + return queue + } + + override fun onClose() {} +} diff --git a/zenoh-java/src/jvmMain/kotlin/io/zenoh/Target.kt b/zenoh-java/src/jvmMain/kotlin/io/zenoh/Target.kt new file mode 100644 index 00000000..4bb087ee --- /dev/null +++ b/zenoh-java/src/jvmMain/kotlin/io/zenoh/Target.kt @@ -0,0 +1,33 @@ +// +// 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 + +enum class Target { + WINDOWS_X86_64_MSVC, + LINUX_X86_64, + LINUX_AARCH64, + APPLE_AARCH64, + APPLE_X86_64; + + override fun toString(): String { + return when (this) { + WINDOWS_X86_64_MSVC -> "x86_64-pc-windows-msvc" + LINUX_X86_64 -> "x86_64-unknown-linux-gnu" + LINUX_AARCH64 -> "aarch64-unknown-linux-gnu" + APPLE_AARCH64 -> "aarch64-apple-darwin" + APPLE_X86_64 -> "x86_64-apple-darwin" + } + } +} diff --git a/zenoh-java/src/jvmMain/kotlin/io/zenoh/Zenoh.kt b/zenoh-java/src/jvmMain/kotlin/io/zenoh/Zenoh.kt new file mode 100644 index 00000000..874d3d23 --- /dev/null +++ b/zenoh-java/src/jvmMain/kotlin/io/zenoh/Zenoh.kt @@ -0,0 +1,181 @@ +// +// 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 java.io.File +import java.io.FileOutputStream +import java.io.InputStream +import java.io.FileInputStream +import java.util.zip.ZipInputStream + +/** + * Static singleton class to load the Zenoh native library once and only once, as well as the logger in function of the + * log level configuration. + */ +internal actual class Zenoh private actual constructor() { + + actual companion object { + private const val ZENOH_LIB_NAME = "zenoh_jni" + private const val ZENOH_LOGS_PROPERTY = "zenoh.logger" + + private var instance: Zenoh? = null + + actual fun load() { + instance ?: Zenoh().also { instance = it } + } + + /** + * Determine target + * + * Determines the [Target] corresponding to the machine on top of which the native code will run. + * + * @return A result with the target. + */ + private fun determineTarget(): Result = runCatching { + val osName = System.getProperty("os.name").lowercase() + val osArch = System.getProperty("os.arch") + + val target = when { + osName.contains("win") -> when { + osArch.contains("x86_64") -> Target.WINDOWS_X86_64_MSVC + else -> throw UnsupportedOperationException("Unsupported architecture: $osArch") + } + + osName.contains("mac") -> when { + osArch.contains("x86_64") -> Target.APPLE_X86_64 + osArch.contains("aarch64") -> Target.APPLE_AARCH64 + else -> throw UnsupportedOperationException("Unsupported architecture: $osArch") + } + + osName.contains("nix") || osName.contains("nux") || osName.contains("aix") -> when { + osArch.contains("x86_64") -> Target.LINUX_X86_64 + osArch.contains("aarch64") -> Target.LINUX_AARCH64 + else -> throw UnsupportedOperationException("Unsupported architecture: $osArch") + } + + else -> throw UnsupportedOperationException("Unsupported platform: $osName") + } + return Result.success(target) + } + + /** + * Unzip library. + * + * The Zenoh libraries are stored within the JAR as compressed ZIP files. + * The location of the zipped files is expected to be under target/target.zip. + * It is expected that the zip file only contains the compressed library. + * + * The uncompressed library will be stored temporarily and deleted on exit. + * + * @param compressedLib Input stream pointing to the compressed library. + * @return A result with the uncompressed library file. + */ + private fun unzipLibrary(compressedLib: InputStream): Result = runCatching { + val zipInputStream = ZipInputStream(compressedLib) + val buffer = ByteArray(1024) + val zipEntry = zipInputStream.nextEntry + + val library = File.createTempFile(zipEntry!!.name, ".tmp") + library.deleteOnExit() + + val parent = library.parentFile + if (!parent.exists()) { + parent.mkdirs() + } + + val fileOutputStream = FileOutputStream(library) + var len: Int + while (zipInputStream.read(buffer).also { len = it } > 0) { + fileOutputStream.write(buffer, 0, len) + } + fileOutputStream.close() + + zipInputStream.closeEntry() + zipInputStream.close() + return Result.success(library) + } + + private fun loadLibraryAsInputStream(target: Target): Result = runCatching { + val libUrl = ClassLoader.getSystemClassLoader().getResourceAsStream("$target/$target.zip")!! + val uncompressedLibFile = unzipLibrary(libUrl) + return Result.success(FileInputStream(uncompressedLibFile.getOrThrow())) + } + + @Suppress("UnsafeDynamicallyLoadedCode") + private fun loadZenohJNI(inputStream: InputStream) { + val tempLib = File.createTempFile("tempLib", "") + tempLib.deleteOnExit() + + FileOutputStream(tempLib).use { output -> + inputStream.copyTo(output) + } + + System.load(tempLib.absolutePath) + } + + /** + * Load library from jar package. + * + * Attempts to load the library corresponding to the `target` specified from the zenoh kotlin jar. + * + * @param target + */ + private fun tryLoadingLibraryFromJarPackage(target: Target): Result = runCatching { + val lib: Result = loadLibraryAsInputStream(target) + lib.onSuccess { loadZenohJNI(it) }.onFailure { throw Exception("Unable to load Zenoh JNI: $it") } + } + + /** + * Try loading local library. + * + * This function aims to load the default library that is usually included when building the zenoh kotlin library + * locally. + */ + private fun tryLoadingLocalLibrary(): Result = runCatching { + val lib = ClassLoader.getSystemClassLoader().findLibraryStream(ZENOH_LIB_NAME) + if (lib != null) { + loadZenohJNI(lib) + } else { + throw Exception("Unable to load local Zenoh JNI.") + } + } + } + + init { + // Try first to load the local native library for cases in which the module was built locally, + // otherwise try to load from the JAR. + if (tryLoadingLocalLibrary().isFailure) { + val target = determineTarget().getOrThrow() + tryLoadingLibraryFromJarPackage(target).getOrThrow() + } + + val logLevel = System.getProperty(ZENOH_LOGS_PROPERTY) + if (logLevel != null) { + Logger.start(logLevel) + } + } +} + +private fun ClassLoader.findLibraryStream(libraryName: String): InputStream? { + val libraryExtensions = listOf(".dylib", ".so", ".dll") + for (extension in libraryExtensions) { + val resourcePath = "lib$libraryName$extension" + val inputStream = getResourceAsStream(resourcePath) + if (inputStream != null) { + return inputStream + } + } + return null +} diff --git a/zenoh-jni/Cargo.lock b/zenoh-jni/Cargo.lock new file mode 100644 index 00000000..8b3f1c40 --- /dev/null +++ b/zenoh-jni/Cargo.lock @@ -0,0 +1,3266 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aes" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c378d78423fdad8089616f827526ee33c19f2fddbd5de1629152c9593ba4783" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + +[[package]] +name = "android-logd-logger" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d7b9303373a56714732e3371513edd14d12987d04ff4f48527444e804bc3ae" +dependencies = [ + "bytes", + "env_logger", + "lazy_static", + "libc", + "log", + "parking_lot", + "redox_syscall 0.3.5", + "thiserror", + "time", + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "array-init" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", + "tokio", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.25", + "slab", + "socket2 0.4.9", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + +[[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", + "futures-lite", + "rustix 0.37.25", + "signal-hook", + "windows-sys 0.48.0", +] + +[[package]] +name = "async-rustls" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29479d362e242e320fa8f5c831940a5b83c1679af014068196cd20d4bf497b6b" +dependencies = [ + "futures-io", + "rustls", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "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" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" + +[[package]] +name = "async-trait" +version = "0.1.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "log", +] + +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" + +[[package]] +name = "cache-padded" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "981520c98f422fcc584dc1a95c334e6953900b9106bc47a9839b81790009eb21" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_lex", + "indexmap 1.9.3", + "strsim", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + +[[package]] +name = "combine" +version = "4.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ed6e9d84f0b51a7f52daf1c7d71dd136fd7a3f41a8462b8cdb8c78d920fad4" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" + +[[package]] +name = "const_format" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48" +dependencies = [ + "const_format_proc_macros", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "dyn-clone" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbfc4744c1b8f2a09adc0e55242f60b1af195d88596bd8700be74418c056c555" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin 0.9.8", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin 0.9.8", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "futures-task" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" + +[[package]] +name = "futures-util" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + +[[package]] +name = "git-version" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b0decc02f4636b9ccad390dcbe77b722a77efedfa393caf8379a51d5c61899" +dependencies = [ + "git-version-macro", + "proc-macro-hack", +] + +[[package]] +name = "git-version-macro" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe69f1cbdb6e28af2bac214e943b99ce8a0a06b447d15d3e61161b0423139f3f" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "http" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.2", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "ipnetwork" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi 0.3.2", + "rustix 0.38.13", + "windows-sys 0.48.0", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "json5" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +dependencies = [ + "pest", + "pest_derive", + "serde", +] + +[[package]] +name = "keccak" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f6d5ed8676d904364de097082f4e7d240b571b67989ced0240f08b7f966f940" +dependencies = [ + "cpufeatures", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin 0.5.2", +] + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "libloading" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + +[[package]] +name = "libm" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +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" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ea9b256699eda7b0387ffbc776dd625e28bde3918446381781245b7a50349d8" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.4.0", + "cfg-if", + "libc", +] + +[[package]] +name = "no-std-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43794a0ace135be66a25d3ae77d41b91615fb68ae937f904090203e81f755b65" + +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi 0.3.2", + "libc", +] + +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a54938017eacd63036332b4ae5c8a49fc8c0c1d6d629893057e4f13609edd06" +dependencies = [ + "num-traits", +] + +[[package]] +name = "os_str_bytes" +version = "6.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac" + +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets 0.48.5", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pest" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "pest_meta" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "petgraph" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +dependencies = [ + "fixedbitset", + "indexmap 2.0.0", +] + +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pnet" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "130c5b738eeda2dc5796fe2671e49027e6935e817ab51b930a36ec9e6a206a64" +dependencies = [ + "ipnetwork", + "pnet_base", + "pnet_datalink", + "pnet_packet", + "pnet_sys", + "pnet_transport", +] + +[[package]] +name = "pnet_base" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cf6fb3ab38b68d01ab2aea03ed3d1132b4868fa4e06285f29f16da01c5f4c" +dependencies = [ + "no-std-net", +] + +[[package]] +name = "pnet_datalink" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad5854abf0067ebbd3967f7d45ebc8976ff577ff0c7bd101c4973ae3c70f98fe" +dependencies = [ + "ipnetwork", + "libc", + "pnet_base", + "pnet_sys", + "winapi", +] + +[[package]] +name = "pnet_macros" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688b17499eee04a0408aca0aa5cba5fc86401d7216de8a63fdf7a4c227871804" +dependencies = [ + "proc-macro2", + "quote", + "regex", + "syn 2.0.33", +] + +[[package]] +name = "pnet_macros_support" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eea925b72f4bd37f8eab0f221bbe4c78b63498350c983ffa9dd4bcde7e030f56" +dependencies = [ + "pnet_base", +] + +[[package]] +name = "pnet_packet" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a005825396b7fe7a38a8e288dbc342d5034dac80c15212436424fef8ea90ba" +dependencies = [ + "glob", + "pnet_base", + "pnet_macros", + "pnet_macros_support", +] + +[[package]] +name = "pnet_sys" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "417c0becd1b573f6d544f73671070b039051e5ad819cc64aa96377b536128d00" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "pnet_transport" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2637e14d7de974ee2f74393afccbc8704f3e54e6eb31488715e72481d1662cc3" +dependencies = [ + "libc", + "pnet_base", + "pnet_packet", + "pnet_sys", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "ppv-lite86" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cc2c5017e4b43d5995dcea317bc46c1e09404c0a9664d2908f7f02dfe943d75" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13f81c9a9d574310b8351f8666f5a93ac3b0069c45c28ad52c10291389a7cf9" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-native-certs", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "055b4e778e8feb9f93c4e439f71dc2156ef13360b432b799e179a8c4cdf0b1d7" +dependencies = [ + "bytes", + "libc", + "socket2 0.5.4", + "tracing", + "windows-sys 0.48.0", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall 0.2.16", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "ringbuffer-spsc" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1938faa63a2362ee1747afb2d10567d0fb1413b9cbd6198a8541485c4f773" +dependencies = [ + "array-init", + "cache-padded", +] + +[[package]] +name = "rsa" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" +dependencies = [ + "byteorder", + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-iter", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustix" +version = "0.38.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7db8590df6dfcd144d22afd1b83b36c21a18d7cbc1dc4bb5295a8712e9eb662" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys 0.4.7", + "windows-sys 0.48.0", +] + +[[package]] +name = "rustls" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +dependencies = [ + "log", + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-native-certs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a27e3b59326c16e23d30aeb7a36a24cc0d29e71d68ff611cdfb4a01d013bed" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "schemars" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763f8cd0d4c71ed8389c90cb8100cba87e763bd01a8e614d4f0af97bcd50a161" +dependencies = [ + "dyn-clone", + "schemars_derive", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0f696e21e10fa546b7ffb1c9672c6de8fbc7a81acf59524386d8639bf12737" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 1.0.109", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "serde", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "serde_derive_internals" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85bf8229e7920a9f636479437026331ce11aa132b4dde37d121944a44d6e5f3c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" +dependencies = [ + "indexmap 2.0.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shellexpand" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da03fa3b94cc19e3ebfc88c4229c49d8f08cdbd1228870a45f0ffdf84988e14b" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" +dependencies = [ + "digest", + "rand_core", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +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", +] + +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stop-token" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af91f480ee899ab2d9f8435bfdfc14d08a5754bd9d3fef1f1a1c23336aad6c8b" +dependencies = [ + "async-channel", + "cfg-if", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" + +[[package]] +name = "thiserror" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "time" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" +dependencies = [ + "deranged", + "itoa", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" + +[[package]] +name = "time-macros" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a942f44339478ef67935ab2bbaec2fb0322496cf3cbe84b261e06ac3814c572" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "token-cell" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a2b964fdb303b08a4eab04d7c1bad2bca33f8eee334ccd28802f1041c6eb87" +dependencies = [ + "paste", +] + +[[package]] +name = "tokio" +version = "1.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "socket2 0.5.4", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2dbec703c26b00d74844519606ef15d09a7d6857860f84ad223dec002ddea2" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "tracing-core" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tungstenite" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uhlc" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1eadef1fa26cbbae1276c46781e8f4d888bdda434779c18ae6c2a0e69991885" +dependencies = [ + "humantime", + "lazy_static", + "log", + "rand", + "serde", + "spin 0.9.8", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "unzip-n" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e7e85a0596447f0f2ac090e16bc4c516c6fe91771fb0c0ccf7fa3dae896b9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "getrandom", +] + +[[package]] +name = "validated_struct" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feef04c049b4beae3037a2a31b8da40d8cebec0b97456f24c7de0ede4ed9efed" +dependencies = [ + "json5", + "serde", + "serde_json", + "validated_struct_macros", +] + +[[package]] +name = "validated_struct_macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d4444a980afa9ef0d29c2a3f4d952ec0495a7a996a9c78b52698b71bc21edb4" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "unzip-n", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.33", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +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.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "zenoh" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-global-executor", + "async-std", + "async-trait", + "base64", + "const_format", + "env_logger", + "event-listener", + "flume 0.11.0", + "form_urlencoded", + "futures", + "git-version", + "hex", + "lazy_static", + "log", + "ordered-float", + "paste", + "petgraph", + "rand", + "regex", + "rustc_version", + "serde", + "serde_json", + "socket2 0.5.4", + "stop-token", + "uhlc", + "uuid", + "vec_map", + "zenoh-buffers", + "zenoh-codec", + "zenoh-collections", + "zenoh-config", + "zenoh-core", + "zenoh-crypto", + "zenoh-link", + "zenoh-macros", + "zenoh-plugin-trait", + "zenoh-protocol", + "zenoh-result", + "zenoh-sync", + "zenoh-transport", + "zenoh-util", +] + +[[package]] +name = "zenoh-buffers" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "zenoh-collections", +] + +[[package]] +name = "zenoh-codec" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "log", + "serde", + "uhlc", + "zenoh-buffers", + "zenoh-protocol", +] + +[[package]] +name = "zenoh-collections" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" + +[[package]] +name = "zenoh-config" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "flume 0.11.0", + "json5", + "num_cpus", + "secrecy", + "serde", + "serde_json", + "serde_yaml", + "validated_struct", + "zenoh-core", + "zenoh-protocol", + "zenoh-result", + "zenoh-util", +] + +[[package]] +name = "zenoh-core" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "lazy_static", + "zenoh-result", +] + +[[package]] +name = "zenoh-crypto" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "aes", + "hmac", + "rand", + "rand_chacha", + "sha3", + "zenoh-result", +] + +[[package]] +name = "zenoh-ext" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "bincode", + "env_logger", + "flume 0.11.0", + "futures", + "log", + "serde", + "zenoh", + "zenoh-core", + "zenoh-macros", + "zenoh-result", + "zenoh-sync", + "zenoh-util", +] + +[[package]] +name = "zenoh-keyexpr" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "hashbrown 0.14.0", + "keyed-set", + "rand", + "schemars", + "serde", + "token-cell", + "zenoh-result", +] + +[[package]] +name = "zenoh-link" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "async-trait", + "zenoh-config", + "zenoh-link-commons", + "zenoh-link-quic", + "zenoh-link-tcp", + "zenoh-link-tls", + "zenoh-link-udp", + "zenoh-link-unixsock_stream", + "zenoh-link-ws", + "zenoh-protocol", + "zenoh-result", +] + +[[package]] +name = "zenoh-link-commons" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "async-trait", + "flume 0.11.0", + "lz4_flex", + "serde", + "typenum", + "zenoh-buffers", + "zenoh-codec", + "zenoh-protocol", + "zenoh-result", +] + +[[package]] +name = "zenoh-link-quic" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-rustls", + "async-std", + "async-trait", + "base64", + "futures", + "log", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pemfile", + "rustls-webpki", + "secrecy", + "zenoh-config", + "zenoh-core", + "zenoh-link-commons", + "zenoh-protocol", + "zenoh-result", + "zenoh-sync", + "zenoh-util", +] + +[[package]] +name = "zenoh-link-tcp" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "async-trait", + "log", + "zenoh-core", + "zenoh-link-commons", + "zenoh-protocol", + "zenoh-result", + "zenoh-sync", + "zenoh-util", +] + +[[package]] +name = "zenoh-link-tls" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-rustls", + "async-std", + "async-trait", + "base64", + "futures", + "log", + "rustls", + "rustls-pemfile", + "rustls-webpki", + "secrecy", + "webpki-roots", + "zenoh-config", + "zenoh-core", + "zenoh-link-commons", + "zenoh-protocol", + "zenoh-result", + "zenoh-sync", + "zenoh-util", +] + +[[package]] +name = "zenoh-link-udp" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "async-trait", + "log", + "socket2 0.5.4", + "zenoh-buffers", + "zenoh-collections", + "zenoh-core", + "zenoh-link-commons", + "zenoh-protocol", + "zenoh-result", + "zenoh-sync", + "zenoh-util", +] + +[[package]] +name = "zenoh-link-unixsock_stream" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "async-trait", + "futures", + "log", + "nix", + "uuid", + "zenoh-core", + "zenoh-link-commons", + "zenoh-protocol", + "zenoh-result", + "zenoh-sync", +] + +[[package]] +name = "zenoh-link-ws" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "async-trait", + "futures-util", + "log", + "tokio", + "tokio-tungstenite", + "url", + "zenoh-core", + "zenoh-link-commons", + "zenoh-protocol", + "zenoh-result", + "zenoh-sync", + "zenoh-util", +] + +[[package]] +name = "zenoh-macros" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.33", + "unzip-n", + "zenoh-keyexpr", +] + +[[package]] +name = "zenoh-plugin-trait" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "libloading", + "log", + "serde_json", + "zenoh-macros", + "zenoh-result", + "zenoh-util", +] + +[[package]] +name = "zenoh-protocol" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "const_format", + "hex", + "rand", + "serde", + "uhlc", + "uuid", + "zenoh-buffers", + "zenoh-keyexpr", + "zenoh-result", +] + +[[package]] +name = "zenoh-result" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "anyhow", +] + +[[package]] +name = "zenoh-sync" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "event-listener", + "flume 0.11.0", + "futures", + "tokio", + "zenoh-buffers", + "zenoh-collections", + "zenoh-core", +] + +[[package]] +name = "zenoh-transport" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-executor", + "async-global-executor", + "async-std", + "async-trait", + "flume 0.11.0", + "log", + "lz4_flex", + "paste", + "rand", + "ringbuffer-spsc", + "rsa", + "serde", + "sha3", + "zenoh-buffers", + "zenoh-codec", + "zenoh-collections", + "zenoh-config", + "zenoh-core", + "zenoh-crypto", + "zenoh-link", + "zenoh-protocol", + "zenoh-result", + "zenoh-sync", + "zenoh-util", +] + +[[package]] +name = "zenoh-util" +version = "0.11.0-dev" +source = "git+https://github.com/eclipse-zenoh/zenoh.git?branch=master#c0ebfff664dd925e9e16d148239eba1ade0ade06" +dependencies = [ + "async-std", + "async-trait", + "clap", + "const_format", + "flume 0.11.0", + "futures", + "hex", + "home", + "humantime", + "lazy_static", + "libc", + "libloading", + "log", + "pnet", + "pnet_datalink", + "shellexpand", + "winapi", + "zenoh-core", + "zenoh-protocol", + "zenoh-result", +] + +[[package]] +name = "zenoh_jni" +version = "0.11.0-dev" +dependencies = [ + "android-logd-logger", + "async-std", + "clap", + "env_logger", + "flume 0.10.14", + "jni", + "json5", + "log", + "rustc_version", + "uhlc", + "zenoh", + "zenoh-ext", +] + +[[package]] +name = "zeroize" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" diff --git a/zenoh-jni/Cargo.toml b/zenoh-jni/Cargo.toml new file mode 100644 index 00000000..bbe4cfe6 --- /dev/null +++ b/zenoh-jni/Cargo.toml @@ -0,0 +1,60 @@ +# +# 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] +rust-version = "1.66.1" +version = "0.11.0-dev" # Zenoh version +repository = "https://github.com/eclipse-zenoh/zenoh" +homepage = "http://zenoh.io" +edition = "2021" +license = "EPL-2.0 OR Apache-2.0" +categories = ["network-programming"] +description = "Zenoh: Zero Overhead Pub/sub, Store/Query and Compute." +name = "zenoh_jni" + +[features] +default = ["zenoh/default", "zenoh-ext/default"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[dependencies] +android-logd-logger = "0.4.0" +async-std = { version = "=1.12.0", default-features = false } +log = "0.4.17" +env_logger = "0.10.0" +clap = "3.2.23" +jni = "0.21.1" +flume = "0.10.14" +uhlc = "0.6.0" +json5 = "0.4.1" +zenoh = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "master", default-features = false } +zenoh-ext = { git = "https://github.com/eclipse-zenoh/zenoh.git", branch = "master", default-features = false } + +[lib] +name = "zenoh_jni" +crate_type = ["staticlib", "dylib"] + +[build-dependencies] +rustc_version = "0.4.0" + +[package.metadata.deb] +name = "zenoh_jni" +maintainer = "zenoh@zettascale.tech" +copyright = "2023 ZettaScale Technology" + +[profile.release] +debug = false # If you want debug symbol in release mode, set the env variable: RUSTFLAGS=-g +lto = "fat" +codegen-units = 1 +opt-level = 3 +panic = "abort" diff --git a/zenoh-jni/src/errors.rs b/zenoh-jni/src/errors.rs new file mode 100644 index 00000000..337a3bab --- /dev/null +++ b/zenoh-jni/src/errors.rs @@ -0,0 +1,56 @@ +// +// 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::fmt; + +use jni::JNIEnv; + +pub(crate) type Result = core::result::Result; + +#[derive(Debug)] +pub(crate) enum Error { + Session(String), + KeyExpr(String), + Jni(String), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Session(msg) => write!(f, "{}", msg), + Error::KeyExpr(msg) => write!(f, "{}", msg), + Error::Jni(msg) => write!(f, "{}", msg), + } + } +} + +impl Error { + fn get_associated_kotlin_exception(&self) -> String { + let class = match self { + Error::Session(_) => "io/zenoh/exceptions/SessionException", + Error::KeyExpr(_) => "io/zenoh/exceptions/KeyExprException", + Error::Jni(_) => "io/zenoh/exceptions/JNIException", + }; + class.to_string() + } + + pub fn throw_on_jvm(&self, env: &mut JNIEnv) -> Result<()> { + 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)))?; + env.throw_new(exception_class, self.to_string()) + .map_err(|err| Error::Jni(format!("Failed to throw exception: {}", err))) + } +} diff --git a/zenoh-jni/src/key_expr.rs b/zenoh-jni/src/key_expr.rs new file mode 100644 index 00000000..9f8335c4 --- /dev/null +++ b/zenoh-jni/src/key_expr.rs @@ -0,0 +1,154 @@ +// +// 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::ptr::null; +use std::sync::Arc; + +use jni::objects::JClass; +use jni::sys::{jboolean, jstring}; +use jni::{objects::JString, JNIEnv}; +use zenoh::prelude::KeyExpr; + +use crate::errors::Error; +use crate::utils::decode_string; + +#[no_mangle] +#[allow(non_snake_case)] +pub(crate) unsafe 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)) +} + +#[no_mangle] +#[allow(non_snake_case)] +pub(crate) unsafe 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)) +} + +#[no_mangle] +#[allow(non_snake_case)] +pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_getStringValueViaJNI( + 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() +} + +#[no_mangle] +#[allow(non_snake_case)] +pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_intersectsViaJNI( + _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 intersects = key_expr_1.intersects(&key_expr_2); + std::mem::forget(key_expr_1); + std::mem::forget(key_expr_2); + intersects as jboolean +} + +#[no_mangle] +#[allow(non_snake_case)] +pub(crate) unsafe extern "C" fn Java_io_zenoh_jni_JNIKeyExpr_includesViaJNI( + _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 +} + +#[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 +} + +#[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); +} diff --git a/zenoh-jni/src/lib.rs b/zenoh-jni/src/lib.rs new file mode 100644 index 00000000..521d2c46 --- /dev/null +++ b/zenoh-jni/src/lib.rs @@ -0,0 +1,34 @@ +// +// 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, +// + +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] +#[cfg(not(feature = "default"))] +fn test_no_default_features() { + assert_eq!(zenoh::FEATURES, concat!(" zenoh/unstable")); +} diff --git a/zenoh-jni/src/logger.rs b/zenoh-jni/src/logger.rs new file mode 100644 index 00000000..767157c9 --- /dev/null +++ b/zenoh-jni/src/logger.rs @@ -0,0 +1,84 @@ +// +// 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 jni::{ + objects::{JClass, JString}, + JNIEnv, +}; + +use crate::errors::{Error, Result}; + +/// Redirects the Rust logs either to logcat for Android systems or to the standard output (for non-Android systems). +/// +/// This function is meant to be called from Java/Kotlin code through JNI. It takes a `log_level` +/// indicating the desired log level, which must be one of the following: "info", "debug", "warn", +/// "trace", or "error". +/// +/// Parameters: +/// - `env`: The JNI environment. +/// - `_class`: The JNI class. +/// - `log_level`: The log level java string indicating the desired log level. +/// +/// Errors: +/// - If there is an error parsing the log level string, a `JNIException` is thrown on the JVM. +/// +#[no_mangle] +#[allow(non_snake_case)] +pub extern "C" fn Java_io_zenoh_Logger_00024Companion_start( + mut env: JNIEnv, + _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| { + log::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(); +} + +/// 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()))?; + log_level + .to_str() + .map(|level| Ok(level.to_string())) + .map_err(|err| Error::Jni(err.to_string()))? +} diff --git a/zenoh-jni/src/publisher.rs b/zenoh-jni/src/publisher.rs new file mode 100644 index 00000000..227929b2 --- /dev/null +++ b/zenoh-jni/src/publisher.rs @@ -0,0 +1,376 @@ +// +// 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::{ops::Deref, sync::Arc}; + +use jni::{ + objects::{JByteArray, JClass}, + sys::jint, + JNIEnv, +}; +use zenoh::{ + prelude::{sync::SyncResolve, KeyExpr}, + publication::Publisher, + Session, +}; + +use crate::{ + errors::{Error, Result}, + sample::decode_sample_kind, +}; +use crate::{ + put::{decode_congestion_control, decode_priority}, + value::decode_value, +}; +use zenoh::SessionDeclarations; + +/// 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. +/// - `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. +/// +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_putViaJNI( + mut env: JNIEnv, + _class: JClass, + payload: JByteArray, + encoding: jint, + ptr: *const Publisher<'static>, +) { + let publisher = Arc::from_raw(ptr); + match perform_put(&env, payload, encoding, publisher.clone()) { + Ok(_) => {} + Err(err) => { + _ = err.throw_on_jvm(&mut env).map_err(|err| { + log::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) => { + log::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. +/// - `publisher`: The Zenoh publisher. +/// +/// Returns: +/// - A [Result] indicating the success or failure of the operation. +/// +fn perform_put( + env: &JNIEnv, + payload: JByteArray, + encoding: jint, + publisher: Arc, +) -> Result<()> { + let value = decode_value(env, payload, encoding)?; + publisher + .put(value) + .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>, +) { + let congestion_control = match decode_congestion_control(congestion_control) { + Ok(congestion_control) => congestion_control, + Err(err) => { + _ = err.throw_on_jvm(env); + return; + } + }; + log::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), + ); + } +} + +/// Modifies the priority 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). +/// - `priority`: The [zenoh::Priority] 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 priority fails to be decoded. +/// +#[no_mangle] +#[allow(non_snake_case)] +pub unsafe extern "C" fn Java_io_zenoh_jni_JNIPublisher_setPriorityViaJNI( + env: &mut JNIEnv, + _class: JClass, + priority: jint, + ptr: *const Publisher<'static>, +) { + let priority = match decode_priority(priority) { + Ok(priority) => priority, + Err(err) => { + _ = err.throw_on_jvm(env); + return; + } + }; + log::debug!("Setting publisher priority with '{priority:?}'."); + unsafe { + let publisher = core::ptr::read(ptr); + core::ptr::write(ptr as *mut _, publisher.priority(priority)); + } +} + +/// Performs a WRITE operation via JNI using the specified Zenoh publisher. +/// +/// Parameters: +/// - `env`: The JNI environment. +/// - `payload`: The payload as a `JByteArray`. +/// - `encoding`: The [zenoh::Encoding] of the payload. +/// - `sample_kind`: The [zenoh::SampleKind] to use. +/// - `publisher`: The Zenoh [Publisher]. +/// +/// Returns: +/// - A [Result] indicating the success or failure of the operation. +/// +fn perform_write( + env: &JNIEnv, + payload: JByteArray, + encoding: jint, + sample_kind: jint, + publisher: Arc, +) -> Result<()> { + let value = decode_value(env, payload, encoding)?; + let sample_kind = decode_sample_kind(sample_kind)?; + publisher + .write(sample_kind, value) + .res() + .map_err(|err| Error::Session(format!("{}", err))) +} + +/// Performs a WRITE 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`](zenoh::Encoding) of the payload. +/// - `sample_kind`: The [`kind`](zenoh::SampleKind) to use. +/// - `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 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_JNIPublisher_writeViaJNI( + mut env: JNIEnv, + _class: JClass, + payload: JByteArray, + encoding: jint, + sample_kind: jint, + ptr: *const Publisher<'static>, +) { + let publisher = Arc::from_raw(ptr); + match perform_write(&env, payload, encoding, sample_kind, publisher.clone()) { + Ok(_) => {} + Err(err) => { + _ = err.throw_on_jvm(&mut env).map_err(|err| { + log::error!( + "Unable to throw exception on WRITE operation failure: {}", + err + ) + }); + } + }; + std::mem::forget(publisher) +} + +/// Performs a DELETE operation via JNI using the specified Zenoh publisher. +/// +/// Parameters: +/// - `publisher`: The Zenoh [Publisher]. +/// +/// Returns: +/// - A [Result] indicating the success or failure of the operation. +/// +fn perform_delete(publisher: Arc) -> Result<()> { + publisher + .delete() + .res() + .map_err(|err| Error::Session(format!("{}", err))) +} + +/// Performs a DELETE 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. +/// - `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 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_JNIPublisher_deleteViaJNI( + mut env: JNIEnv, + _class: JClass, + ptr: *const Publisher<'static>, +) { + let publisher = Arc::from_raw(ptr); + match perform_delete(publisher.clone()) { + Ok(_) => {} + Err(err) => { + _ = err.throw_on_jvm(&mut env).map_err(|err| { + log::error!( + "Unable to throw exception on WRITE operation failure: {}", + err + ) + }); + } + }; + std::mem::forget(publisher) +} diff --git a/zenoh-jni/src/put.rs b/zenoh-jni/src/put.rs new file mode 100644 index 00000000..a370b2da --- /dev/null +++ b/zenoh-jni/src/put.rs @@ -0,0 +1,104 @@ +// +// 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::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. +/// +/// 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, +) -> 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) => { + log::warn!( + "Error decoding congestion control: '{}'. Using default...", + err + ); + CongestionControl::default() + } + }; + + let priority = match decode_priority(priority) { + Ok(priority) => priority, + Err(err) => { + log::warn!("Error decoding priority: '{}'. Using default...", err); + Priority::default() + } + }; + + let key_expr_clone = key_expr.deref().clone(); + match session + .put(key_expr_clone, value.to_owned()) + .kind(sample_kind) + .congestion_control(congestion_control) + .priority(priority) + .res() + { + Ok(_) => { + log::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 new file mode 100644 index 00000000..af3a88bc --- /dev/null +++ b/zenoh-jni/src/query.rs @@ -0,0 +1,289 @@ +// +// 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::{mem, ops::Deref, sync::Arc}; + +use jni::{ + objects::{GlobalRef, JByteArray, JClass, JPrimitiveArray, JValue}, + sys::{jboolean, jint, jlong}, + JNIEnv, +}; +use zenoh::{ + prelude::{sync::SyncResolve, KeyExpr, SplitBuffer}, + query::{ConsolidationMode, QueryTarget}, + queryable::Query, + sample::Sample, + value::Value, +}; + +use crate::sample::decode_sample; +use crate::{ + errors::{Error, Result}, + value::decode_value, +}; + +/// Replies with success to a Zenoh query via JNI. +/// +/// This function is meant to be called from Java/Kotlin through JNI. +/// +/// 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. +/// - `timestamp_enabled`: A boolean indicating whether the timestamp is enabled. +/// - `timestamp_ntp_64`: The NTP64 timestamp value. +/// +/// 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. +/// +#[no_mangle] +#[allow(non_snake_case)] +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>, + payload: JByteArray, + encoding: jint, + sample_kind: jint, + timestamp_enabled: jboolean, + timestamp_ntp_64: jlong, +) { + 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| { + log::error!("Unable to throw exception on query reply failure. {}", err) + }); + return; + } + }; + + let query = Arc::from_raw(query_ptr); + query_reply(&query, Ok(sample), env); + mem::forget(query) +} + +/// 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. +/// +/// 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. +/// +/// 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. +/// +#[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, + payload: JByteArray, + encoding: jint, +) { + let errorValue = match decode_value(&env, payload, encoding) { + Ok(value) => value, + Err(err) => { + _ = err.throw_on_jvm(&mut env).map_err(|err| { + log::error!("Unable to throw exception on query reply failure. {}", err) + }); + return; + } + }; + + let query = Arc::from_raw(ptr); + query_reply(&query, Err(errorValue), env); + mem::forget(query) +} + +/// 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. +/// +/// Parameters: +/// - `_env`: The JNI environment. +/// - `_class`: The JNI class. +/// - `ptr`: The raw pointer to the Zenoh query ([Query]). +/// +/// Safety: +/// - The function is marked as unsafe due to raw pointer manipulation. +/// - It assumes that the provided query 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 query 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_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 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[BIJ)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(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| log::error!("Error deleting local ref: {}", err)); + _ = env + .delete_local_ref(payload) + .map_err(|err| log::error!("Error deleting local ref: {}", err)); + result +} + +/// Helper function to perform a reply to a query. +fn query_reply(query: &Arc, reply: core::result::Result, mut env: JNIEnv) { + match query + .reply(reply) + .res() + .map_err(|err| Error::Session(err.to_string())) + { + Ok(_) => {} + Err(err) => { + _ = err.throw_on_jvm(&mut env).map_err(|err| { + log::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}" + ))), + } +} diff --git a/zenoh-jni/src/queryable.rs b/zenoh-jni/src/queryable.rs new file mode 100644 index 00000000..60031655 --- /dev/null +++ b/zenoh-jni/src/queryable.rs @@ -0,0 +1,118 @@ +// +// 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::{ops::Deref, sync::Arc}; + +use jni::{ + objects::{JClass, JObject}, + sys::jboolean, + JNIEnv, +}; +use zenoh::prelude::r#sync::*; +use zenoh::{queryable::Queryable, Session}; + +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. +/// +/// Parameters: +/// - `_env`: The JNI environment. +/// - `_class`: The JNI class. +/// - `ptr`: The raw pointer to the Zenoh queryable ([Queryable]). +/// +/// 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. +/// - After calling this function, the queryable 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_JNIQueryable_freePtrViaJNI( + _env: JNIEnv, + _: JClass, + ptr: *const zenoh::queryable::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(); + log::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) => { + log::error!("Unable to attach thread for queryable callback: {}", err); + return; + } + }; + + log::debug!("Receiving query through JNI: {}", query.to_string()); + match on_query(env, query, &callback_global_ref) { + Ok(_) => log::debug!("Queryable callback called successfully."), + Err(err) => log::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))) +} diff --git a/zenoh-jni/src/reply.rs b/zenoh-jni/src/reply.rs new file mode 100644 index 00000000..753dd178 --- /dev/null +++ b/zenoh-jni/src/reply.rs @@ -0,0 +1,140 @@ +// +// 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::Error; +use crate::errors::Result; + +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 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)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), + ], + ) { + 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| log::debug!("Error deleting local ref: {}", err)); + _ = env + .delete_local_ref(byte_array) + .map_err(|err| log::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| log::debug!("Error deleting local ref: {}", err)); + _ = env + .delete_local_ref(byte_array) + .map_err(|err| log::debug!("Error deleting local ref: {}", err)); + result +} diff --git a/zenoh-jni/src/sample.rs b/zenoh-jni/src/sample.rs new file mode 100644 index 00000000..1d8dd23f --- /dev/null +++ b/zenoh-jni/src/sample.rs @@ -0,0 +1,60 @@ +// +// 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 new file mode 100644 index 00000000..02d498e1 --- /dev/null +++ b/zenoh-jni/src/session.rs @@ -0,0 +1,746 @@ +// +// 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::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_string, get_callback_global_ref, get_java_vm, load_on_close}; +use crate::value::decode_value; + +use jni::objects::{JByteArray, JClass, JObject, JString}; +use jni::sys::{jboolean, jint, jlong}; +use jni::JNIEnv; +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::*; + +/// Open a Zenoh session via JNI. +/// +/// This function is meant to be called from Java/Kotlin through JNI. +/// +/// 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. +/// +#[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 { + let session = open_session(&mut env, config_path); + match session { + Ok(session) => Arc::into_raw(Arc::new(session)), + Err(err) => { + log::error!("Unable to open session: {}", err); + _ = Error::Session(err.to_string()) + .throw_on_jvm(&mut env) + .map_err(|err| { + log::error!("Unable to throw exception on session failure: {}", err) + }); + null() + } + } +} + +/// Open a Zenoh session via JNI. +/// +/// This function is meant to be called from Java/Kotlin through JNI. +/// +/// 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. +/// +#[no_mangle] +#[allow(non_snake_case)] +pub extern "C" fn Java_io_zenoh_jni_JNISession_openSessionWithJsonConfigViaJNI( + mut env: JNIEnv, + _class: JClass, + json_config: JString, +) -> *const zenoh::Session { + let session = open_session_with_json_config(&mut env, json_config); + match session { + Ok(session) => Arc::into_raw(Arc::new(session)), + Err(err) => { + log::error!("Unable to open session: {}", err); + _ = Error::Session(err.to_string()) + .throw_on_jvm(&mut env) + .map_err(|err| { + log::error!("Unable to throw exception on session failure: {}", err) + }); + null() + } + } +} + +/// 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(env: &mut JNIEnv, config_path: JString) -> Result { + let config_file_path = decode_string(env, config_path)?; + let config = if config_file_path.is_empty() { + 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())), + }?; + 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)), + })? + }; + zenoh::open(config) + .res() + .map_err(|err| Error::Session(err.to_string())) +} + +/// Closes a 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. +/// - `ptr`: The raw pointer to the Zenoh session. +/// +/// 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. +/// +#[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, +) { + let ptr = Arc::try_unwrap(Arc::from_raw(ptr)); + match ptr { + Ok(session) => { + // Do nothing, the pointer will be freed. + } + Err(arc_session) => { + let ref_count = Arc::strong_count(&arc_session); + log::error!("Unable to close the session."); + _ = Error::Session(format!( + "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| log::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: +/// - `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: +/// - A raw pointer to the declared Zenoh publisher or null in case of failure. +/// +/// 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. +/// +#[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, + 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| { + log::error!( + "Unable to throw exception on publisher declaration failure. {}", + err + ) + }); + null() + } + } +} + +/// 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. +/// - `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. +/// - `congestion_control`: The [CongestionControl] mechanism specified. +/// - `priority`: The [Priority] mechanism specified. +/// - `sample_kind`: The [SampleKind] of the put operation. +/// +/// 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_putViaJNI( + mut env: JNIEnv, + _class: JClass, + key_expr_ptr: *const KeyExpr<'static>, + session_ptr: *const zenoh::Session, + payload: JByteArray, + encoding: jint, + congestion_control: jint, + priority: jint, + sample_kind: jint, +) { + 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, + ) { + Ok(_) => {} + Err(err) => { + _ = err.throw_on_jvm(&mut env).map_err(|err| { + log::error!( + "Unable to throw exception on query declaration failure. {}", + err + ) + }); + } + } + std::mem::forget(session); + std::mem::forget(key_expr); +} + +/// Declare a Zenoh subscriber 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`: The key expression for the subscriber. +/// - `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. +/// +/// Returns: +/// - A raw pointer to the declared Zenoh subscriber or null in case of failure. +/// +/// 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 callback function passed as `callback` must be a valid instance of the `JNISubscriberCallback` interface +/// in Java/Kotlin, matching the specified signature. +/// - The function may throw a JNI 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_declareSubscriberViaJNI( + mut env: JNIEnv, + _class: JClass, + key_expr_ptr: *const KeyExpr<'static>, + ptr: *const zenoh::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| { + log::error!( + "Unable to throw exception on subscriber declaration failure: {}", + err + ) + }); + null() + } + } +} + +/// Declare a Zenoh queryable 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`: A raw pointer to the [KeyExpr] to be used for the queryable. +/// - `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. +/// +/// 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 callback function passed as `callback` must be a valid instance of the `JNIQueryableCallback` interface +/// in Java/Kotlin, matching the specified signature. +/// - The function may throw a JNI 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_declareQueryableViaJNI( + mut env: JNIEnv, + _class: JClass, + key_expr_ptr: *const KeyExpr<'static>, + session_ptr: *const zenoh::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| { + log::error!( + "Unable to throw exception on query declaration failure. {}", + err + ) + }); + null() + } + } +} + +/// Declare a [KeyExpr] through a [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. +/// - `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. +/// +/// 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 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_declareKeyExprViaJNI( + mut env: JNIEnv, + _class: JClass, + session_ptr: *const zenoh::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| { + log::error!( + "Unable to throw exception on key expr declaration failure. {}", + err + ) + }); + null() + } + } +} + +/// 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. +/// +/// This function is meant to be called from Java/Kotlin code through JNI. +/// +/// 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: +/// - 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 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_undeclareKeyExprViaJNI( + mut env: JNIEnv, + _class: JClass, + session_ptr: *const zenoh::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() { + Ok(_) => {} + Err(err) => { + _ = Error::Session(format!( + "Unable to declare key expression {key_expr}: {}", + err + )) + .throw_on_jvm(&mut env) + } + } + 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. +/// +/// 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, +) { + 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, + ) { + Ok(_) => {} + Err(err) => { + _ = err.throw_on_jvm(&mut env).map_err(|err| { + log::error!( + "Unable to throw exception on get operation failure. {}", + err + ) + }); + } + } + std::mem::forget(session); + std::mem::forget(key_expr); +} + +/// 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: +/// - `env`: The JNI environment. +/// - `_class`: The JNI class. +/// - `key_expr_ptr`: A raw pointer to the [KeyExpr] to be used for the operation. +/// - `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. +/// +/// 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_getWithValueViaJNI( + mut env: JNIEnv, + _class: JClass, + key_expr_ptr: *const KeyExpr<'static>, + selector_params: JString, + session_ptr: *const zenoh::Session, + callback: JObject, + on_close: JObject, + timeout_ms: jlong, + target: jint, + consolidation: jint, + payload: JByteArray, + encoding: jint, +) { + 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)), + ) { + Ok(_) => {} + Err(err) => { + _ = err.throw_on_jvm(&mut env).map_err(|err| { + log::error!( + "Unable to throw exception on get operation failure. {}", + 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". +/// +/// Returns: +/// - A `Result` indicating the result of the `get` operation. +/// +#[allow(clippy::too_many_arguments)] +fn on_get_query( + 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)>, +) -> 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 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 + log::debug!("Receiving reply through JNI: {:?}", reply); + let env = match java_vm.attach_current_thread_as_daemon() { + Ok(env) => env, + Err(err) => { + log::error!("Unable to attach thread for GET query callback: {}", err); + return; + } + }; + match on_reply(env, reply, &callback_global_ref) { + Ok(_) => {} + Err(err) => log::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) + } + + get_builder + .res() + .map(|_| { + log::trace!( + "Performing get on {key_expr:?}, with target '{query_target:?}', with timeout '{timeout:?}', consolidation '{consolidation:?}', with value: '{binding:?}'", + ) + }) + .map_err(|err| Error::Session(err.to_string()))?; + Ok(()) +} + +pub(crate) unsafe fn declare_keyexpr( + 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); + + match result { + Ok(key_expr) => Ok(key_expr), + Err(err) => Err(Error::Session(format!( + "Unable to declare key expression {key_expr}: {}", + err + ))), + } +} diff --git a/zenoh-jni/src/subscriber.rs b/zenoh-jni/src/subscriber.rs new file mode 100644 index 00000000..a1309add --- /dev/null +++ b/zenoh-jni/src/subscriber.rs @@ -0,0 +1,164 @@ +// +// 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::{ops::Deref, sync::Arc}; + +use jni::{ + objects::{JClass, JObject, JValue}, + sys::{jint, jlong}, + JNIEnv, +}; +use zenoh::prelude::r#sync::*; +use zenoh::subscriber::Subscriber; + +use crate::errors::{Error, Result}; +use crate::utils::{get_callback_global_ref, get_java_vm, load_on_close}; + +/// 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. +/// +/// Parameters: +/// - `_env`: The JNI environment. +/// - `_class`: The JNI class. +/// - `ptr`: The raw pointer to the Zenoh subscriber ([Subscriber]). +/// +/// 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. +/// - After calling this function, the subscriber 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_JNISubscriber_freePtrViaJNI( + _env: JNIEnv, + _: JClass, + ptr: *const zenoh::subscriber::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(); + log::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) => { + log::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) => { + log::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 key_expr_ptr = Arc::into_raw(Arc::new(sample.key_expr)); + match env.call_method( + &callback_global_ref, + "run", + "(J[BIIJZ)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), + ], + ) { + Ok(_) => {} + Err(err) => { + log::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| log::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)))?; + + log::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}'" + ))), + } +} diff --git a/zenoh-jni/src/utils.rs b/zenoh-jni/src/utils.rs new file mode 100644 index 00000000..03e886cd --- /dev/null +++ b/zenoh-jni/src/utils.rs @@ -0,0 +1,100 @@ +// +// 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::{JObject, JString}, + JNIEnv, JavaVM, +}; + +use crate::errors::{Error, Result}; + +/// Converts a JString into a rust String. +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)))?; + let value = binding + .to_str() + .map_err(|err| Error::Jni(format!("Error decoding JString: {}", err)))?; + Ok(value.to_string()) +} + +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))) +} + +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 + )) + }) +} + +/// A type that calls a function when dropped +pub(crate) struct CallOnDrop(core::mem::MaybeUninit); +impl CallOnDrop { + /// Constructs a value that calls `f` when dropped. + pub fn new(f: F) -> Self { + Self(core::mem::MaybeUninit::new(f)) + } + /// Does nothing, but tricks closures into moving the value inside, + /// so that the closure's destructor will call `drop(self)`. + pub fn noop(&self) {} +} +impl Drop for CallOnDrop { + fn drop(&mut self) { + // Take ownership of the closure that is always initialized, + // since the only constructor uses `MaybeUninit::new` + let f = unsafe { self.0.assume_init_read() }; + // Call the now owned function + f(); + } +} + +pub(crate) fn load_on_close( + java_vm: &Arc, + on_close_global_ref: jni::objects::GlobalRef, +) -> CallOnDrop { + CallOnDrop::new({ + let java_vm = java_vm.clone(); + move || { + let mut env = match java_vm.attach_current_thread_as_daemon() { + Ok(env) => env, + Err(err) => { + log::error!("Unable to attach thread for 'onClose' callback: {}", err); + return; + } + }; + match env.call_method(on_close_global_ref, "run", "()V", &[]) { + Ok(_) => (), + Err(err) => { + _ = env.exception_describe(); + _ = Error::Jni(format!("Error while running 'onClose' callback: {}", err)) + .throw_on_jvm(&mut env) + .map_err(|err| { + log::error!("Unable to throw exception upon 'onClose' failure: {}", err) + }); + } + } + } + }) +} diff --git a/zenoh-jni/src/value.rs b/zenoh-jni/src/value.rs new file mode 100644 index 00000000..3611c45b --- /dev/null +++ b/zenoh-jni/src/value.rs @@ -0,0 +1,49 @@ +// +// 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 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 payload_len = env + .get_array_length(&payload) + .map(|length| length as usize) + .map_err(|err| Error::Jni(err.to_string()))?; + + let mut buff = vec![0; payload_len]; + env.get_byte_array_region(payload, 0, &mut buff[..]) + .map_err(|err| Error::Jni(err.to_string()))?; + + let buff: Vec = buff.iter().map(|&x| x as u8).collect(); + let encoding = match KnownEncoding::try_from(encoding as u8) { + Ok(encoding) => encoding, + Err(_) => { + log::debug!("Unable to retrieve encoding. Setting Empty encoding."); + KnownEncoding::Empty + } + }; + Ok(build_value(&buff[..], encoding)) +} diff --git a/zenoh/Makefile b/zenoh/Makefile deleted file mode 100644 index f33e59b4..00000000 --- a/zenoh/Makefile +++ /dev/null @@ -1,125 +0,0 @@ -# -# Copyright (c) 2017, 2020 ADLINK Technology Inc. -# -# 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: -# ADLINK zenoh team, -# - -# WARNING: this Makefile has the following assumptions: -# - The host is on MacOS -# - JAVA_HOME environment variable points to the JDK installation dir -# - swig is installed -# - zenoh-c source directory is present in the same directory than zenoh-java - -TARGET_DIR:=target - -ifdef ZENOHC_HOME -ZENOHC_DEV_DIR := $(ZENOHC_HOME) -else -ZENOHC_DEV_DIR := ../../zenoh-c -endif - -ZENOHC_COPY_DIR := $(TARGET_DIR)/zenoh-c -ZENOHC_BUILD_DIR := $(ZENOHC_COPY_DIR)/build -ZENOHC_LIB_NAME := zenohc_java - - -SWIG_FILE := src/main/swig/zenohc.i -SWIG_JAVA_OUTDIR := $(TARGET_DIR)/generated-sources/java/org/eclipse/zenoh/swig -SWIG_C_OUTDIR := $(ZENOHC_COPY_DIR)/src -SWIG_C_FILE := $(SWIG_C_OUTDIR)/zenohc_java.c - -JNI_INCLUDE_ALL_PLATFORMS := $(ZENOHC_COPY_DIR)/jni-include -NATIVES_DIR := $(TARGET_DIR)/resources/natives - - -# CMAKE_OPTIONS := -DSWIG_JAVA=ON -DTESTS=OFF -DEXAMPLES=OFF -# ifneq ($(ZENOH_DEBUG),) -# CMAKE_OPTIONS += -DZENOH_DEBUG=$(ZENOH_DEBUG) -# endif -# ifneq ($(CMAKE_BUILD_TYPE),) -# CMAKE_OPTIONS += -DCMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) -# endif - -# Naive OS detection... TODO: probably to be improved -UNAME_S := $(shell uname -s) -UNAME_M := $(shell uname -m) - -ifeq ($(UNAME_S),Darwin) - OS := osx - LOCAL_LIB_NAME := lib$(ZENOHC_LIB_NAME).dylib -else ifeq ($(UNAME_S),$(filter $(UNAME_S),Linux)) - OS := linux - LOCAL_LIB_NAME := lib$(ZENOHC_LIB_NAME).so -else ifeq ($(UNAME_S),$(filter $(UNAME_S),Windows_NT CYGWIN_NT MINGW)) - OS := windows - LOCAL_LIB_NAME := $(ZENOHC_LIB_NAME).dll -else - $(error "Unsupported platform. (uname -s = $(UNAME_S)") -endif - -ifeq ($(UNAME_M),$(filter $(UNAME_M),i686 i386)) - ARCH := 32 -else ifeq ($(UNAME_M),$(filter $(UNAME_M),x86_64 amd64 )) - ARCH := 64 -else - $(error "Unsupported architecture. (uname -m = $(UNAME_M)") -endif - -LOCAL_LIB := $(ZENOHC_BUILD_DIR)/$(LOCAL_LIB_NAME) - - -all: $(LOCAL_LIB) - @mkdir -p $(NATIVES_DIR)/$(OS)_$(ARCH) - @cp $(LOCAL_LIB) $(NATIVES_DIR)/$(OS)_$(ARCH)/$(LOCAL_LIB_NAME) - -all-cross: all - ZENOH_JAVA=ON JNI_INCLUDE_HOME=jni-include make -C$(ZENOHC_COPY_DIR) manylinux2010-x86 manylinux2010-x64 linux-armv6 linux-arm64 - @mkdir -p $(NATIVES_DIR)/linux_32 - @cp $(ZENOHC_BUILD_DIR)/crossbuilds/manylinux2010-x86/lib* $(NATIVES_DIR)/linux_32 - @mkdir -p $(NATIVES_DIR)/linux_64 - @cp $(ZENOHC_BUILD_DIR)/crossbuilds/manylinux2010-x64/lib* $(NATIVES_DIR)/linux_64 - @mkdir -p $(NATIVES_DIR)/arm - @cp $(ZENOHC_BUILD_DIR)/crossbuilds/linux-armv6/lib* $(NATIVES_DIR)/arm - @mkdir -p $(NATIVES_DIR)/aarch64 - @cp $(ZENOHC_BUILD_DIR)/crossbuilds/linux-arm64/lib* $(NATIVES_DIR)/aarch64 - - -$(LOCAL_LIB): $(ZENOHC_COPY_DIR)/Makefile $(SWIG_C_FILE) $(JNI_INCLUDE_ALL_PLATFORMS)/jni.h - ZENOH_JAVA=ON make -C$(ZENOHC_COPY_DIR) - -$(ZENOHC_COPY_DIR)/Makefile: - @rm -fr $(ZENOHC_COPY_DIR) - @mkdir -p $(ZENOHC_COPY_DIR) - @if [ -z "$(ZENOH_GIT_TAG)" -a -d $(ZENOHC_DEV_DIR) ]; then \ - echo "Found zenoh-c sources in $(ZENOHC_DEV_DIR) . Copy them to $(ZENOHC_COPY_DIR)"; \ - cp -r $(ZENOHC_DEV_DIR)/Makefile $(ZENOHC_DEV_DIR)/CMakeLists.txt $(ZENOHC_DEV_DIR)/include $(ZENOHC_DEV_DIR)/src $(ZENOHC_COPY_DIR)/ ; \ - else \ - echo "Clone tag '$${ZENOH_GIT_TAG:-master}' from Github to $(ZENOHC_COPY_DIR)" ; \ - git clone --branch $${ZENOH_GIT_TAG:-master} https://github.com/eclipse-zenoh/zenoh-c $(ZENOHC_COPY_DIR) ; \ - fi - -$(SWIG_C_FILE): $(ZENOHC_COPY_DIR)/Makefile $(SWIG_FILE) - @mkdir -p $(SWIG_JAVA_OUTDIR) - swig -java -package org.eclipse.zenoh.swig -I$(ZENOHC_COPY_DIR)/include -outdir $(SWIG_JAVA_OUTDIR) -o $(SWIG_C_FILE) $(SWIG_FILE) - - -$(JNI_INCLUDE_ALL_PLATFORMS)/jni.h: - @echo "Retrieve JNI headers for all platforms" - @mkdir -p $(TARGET_DIR)/jni_header - @git clone https://github.com/emabrey/jni-headers.git --depth 1 $(TARGET_DIR)/jni_header - @mkdir -p $(JNI_INCLUDE_ALL_PLATFORMS) - @cp -r $(TARGET_DIR)/jni_header/src/nar/resources/noarch/include/jdk/1.8.0_144-b01/* $(JNI_INCLUDE_ALL_PLATFORMS) - @rm $(JNI_INCLUDE_ALL_PLATFORMS)/jni_md.h - - - -clean: - @rm -fr $(TARGET_DIR) \ No newline at end of file diff --git a/zenoh/pom.xml b/zenoh/pom.xml deleted file mode 100644 index 0d33f875..00000000 --- a/zenoh/pom.xml +++ /dev/null @@ -1,317 +0,0 @@ - - - - 4.0.0 - - - org.eclipse.zenoh - parent-pom - ../parent-pom - 0.4.2-M1 - - - zenoh - jar - Zenoh - The Zenoh client API in Java - - - ${project.build.directory}/generated-sources/java - - - - 0.4.2-M1 - - - - - org.slf4j - slf4j-api - - - org.scijava - native-lib-loader - - - junit - junit - test - - - - ch.qos.logback - logback-core - - - ch.qos.logback - logback-classic - - - - - - - - - - META-INF - ${project.basedir}/.. - - LICENSE - NOTICE.md - - false - - - - ${project.build.directory}/resources - false - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - - add-source - process-resources - - add-source - - - - ${generatedSources} - - - - - - add-it-source - process-resources - - add-test-source - - - - src/it/java - - - - - - - - - - - - not-in-jipp - - - !jipp - - - - - - - org.codehaus.mojo - exec-maven-plugin - - - make-natives - generate-sources - - exec - - - make - - ${zenohc.make-target} - - - ${zenohc.git-tag} - ${zenohc.debug-level} - ${zenohc.build-type} - - ${project.basedir} - - - - - - - - - - - default - - true - - - all - - - - - - - - debug - - all - 2 - Debug - - - - - - release - - all-cross - 0 - Release - - - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar-no-fork - - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - prepare-package - - javadoc-no-fork - jar - - - - - - org/eclipse/zenoh/*.java - - ${project.build.directory}/site/docs/apidocs - none - - - Zenoh client API - org.eclipse.zenoh.* - - -
Zenoh ${project.version}
-
-
-
-
-
- - - - it - - - - org.apache.maven.plugins - maven-failsafe-plugin - - - integration-test - - integration-test - - - - verify - - verify - - - - - - - - - - - codecov - - - - org.jacoco - jacoco-maven-plugin - - - jacoco-prepare-agent - - prepare-agent - - - - jacoco-prepare-agent-integration - - prepare-agent-integration - - - - jacoco-report - - report - - - - jacoco-integration - - report-integration - - - - - - - - -
- -
diff --git a/zenoh/src/it/java/org/eclipse/zenoh/net/test/ClientIT.java b/zenoh/src/it/java/org/eclipse/zenoh/net/test/ClientIT.java deleted file mode 100644 index f9e31a26..00000000 --- a/zenoh/src/it/java/org/eclipse/zenoh/net/test/ClientIT.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -package org.eclipse.zenoh.net.test; - -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import org.eclipse.zenoh.net.*; - -public class ClientIT { - - public static class MVar { - T ref; - T val; - - public synchronized T get() { - try { - while (val == null) { - wait(); - } - T result = val; - val = null; - notify(); - return result; - } catch (InterruptedException e) { - return null; - } - } - - public synchronized void put(T putval) { - try { - while (val != null) { - wait(); - } - val = putval; - ref = putval; - notify(); - } catch (InterruptedException e) { - } - } - - public T read() { - return ref; - } - } - - public static boolean resourceEquals(Resource r1, Resource r2) { - return r1.getRname().equals(r2.getRname()) && r1.getData().equals(r2.getData()); - } - - MVar z1_sub1_last_res = new MVar(); - MVar z2_sub1_last_res = new MVar(); - MVar z3_sub1_last_res = new MVar(); - MVar z1_sto1_last_res = new MVar(); - MVar z2_sto1_last_res = new MVar(); - MVar>> replies = new MVar>>(); - - private class z1_sub1_listener implements DataHandler { - public void handleData(String rname, ByteBuffer data, DataInfo info) { - z1_sub1_last_res.put(new Resource(rname, data, info.getEncoding(), info.getKind())); - } - } - - private class z2_sub1_listener implements DataHandler { - public void handleData(String rname, ByteBuffer data, DataInfo info) { - z2_sub1_last_res.put(new Resource(rname, data, info.getEncoding(), info.getKind())); - } - } - - private class z3_sub1_listener implements DataHandler { - public void handleData(String rname, ByteBuffer data, DataInfo info) { - z3_sub1_last_res.put(new Resource(rname, data, info.getEncoding(), info.getKind())); - } - } - - private class z1_sto1_listener implements StorageHandler { - public void handleData(String rname, ByteBuffer data, DataInfo info) { - z1_sto1_last_res.put(new Resource(rname, data, info.getEncoding(), info.getKind())); - } - - public void handleQuery(String rname, String predicate, RepliesSender repliesSender) { - repliesSender.sendReplies(new Resource[] { z1_sto1_last_res.read() }); - } - } - - private class z2_sto1_listener implements StorageHandler { - public void handleData(String rname, ByteBuffer data, DataInfo info) { - z2_sto1_last_res.put(new Resource(rname, data, info.getEncoding(), info.getKind())); - } - - public void handleQuery(String rname, String predicate, RepliesSender repliesSender) { - repliesSender.sendReplies(new Resource[] { z2_sto1_last_res.read() }); - } - } - - private class z1_eval1_handler implements QueryHandler { - public void handleQuery(String rname, String predicate, RepliesSender repliesSender) { - try { - ByteBuffer data = (ByteBuffer) ByteBuffer.allocateDirect(256).put("z1_eval1_data".getBytes("UTF-8")) - .flip(); - repliesSender.sendReplies(new Resource[] { new Resource("/test/java/client/z1_eval1", data, 0, 0) }); - } catch (Exception e) { - repliesSender.sendReplies(new Resource[0]); - } - } - } - - private class z2_eval1_handler implements QueryHandler { - public void handleQuery(String rname, String predicate, RepliesSender repliesSender) { - try { - ByteBuffer data = (ByteBuffer) ByteBuffer.allocateDirect(256).put("z2_eval1_data".getBytes("UTF-8")) - .flip(); - repliesSender.sendReplies(new Resource[] { new Resource("/test/java/client/z2_eval1", data, 0, 0) }); - } catch (Exception e) { - repliesSender.sendReplies(new Resource[0]); - } - } - } - - private class reply_handler implements ReplyHandler { - List storage_replies = new ArrayList(); - List eval_replies = new ArrayList(); - - public void handleReply(ReplyValue reply) { - switch (reply.getKind()) { - case ZN_STORAGE_DATA: - storage_replies.add(new Resource(reply.getRname(), reply.getData(), reply.getInfo().getKind(), - reply.getInfo().getEncoding())); - break; - case ZN_EVAL_DATA: - eval_replies.add(new Resource(reply.getRname(), reply.getData(), reply.getInfo().getKind(), - reply.getInfo().getEncoding())); - break; - case ZN_REPLY_FINAL: - replies.put(Arrays.asList(storage_replies, eval_replies)); - storage_replies = new ArrayList(); - eval_replies = new ArrayList(); - break; - default: - break; - } - } - } - - @Test - public final void test() throws Exception { - - System.out.println("============================"); - - Session z1 = Session.open("tcp/127.0.0.1:7447"); - Subscriber z1_sub1 = z1.declareSubscriber("/test/java/client/**", SubMode.push(), new z1_sub1_listener()); - Storage z1_sto1 = z1.declareStorage("/test/java/client/**", new z1_sto1_listener()); - Eval z1_eval1 = z1.declareEval("/test/java/client/z1_eval1", new z1_eval1_handler()); - Publisher z1_pub1 = z1.declarePublisher("/test/java/client/z1_pub1"); - - Session z2 = Session.open("tcp/127.0.0.1:7447"); - Subscriber z2_sub1 = z2.declareSubscriber("/test/java/client/**", SubMode.push(), new z2_sub1_listener()); - Storage z2_sto1 = z2.declareStorage("/test/java/client/**", new z2_sto1_listener()); - Eval z2_eval1 = z2.declareEval("/test/java/client/z2_eval1", new z2_eval1_handler()); - Publisher z2_pub1 = z2.declarePublisher("/test/java/client/z2_pub1"); - - Session z3 = Session.open("tcp/127.0.0.1:7447"); - Subscriber z3_sub1 = z3.declareSubscriber("/test/java/client/**", SubMode.pull(), new z3_sub1_listener()); - - Thread.sleep(1000); - - ByteBuffer sent_buf; - Resource sent_res; - Resource rcvd_res; - List> reps; - - sent_buf = (ByteBuffer) ByteBuffer.allocateDirect(256).put("z1_wr1_spl1".getBytes("UTF-8")).flip(); - sent_res = new Resource("/test/java/client/z1_wr1", sent_buf, 0, 0); - z1.writeData(sent_res.getRname(), sent_res.getData().duplicate()); - rcvd_res = z1_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z2_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z1_sto1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z2_sto1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - z3_sub1.pull(); - rcvd_res = z3_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - - z1.query("/test/java/client/**", "", new reply_handler()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - Assert.assertEquals(2, reps.get(1).size()); - - z1.query("/test/java/client/**", "", new reply_handler(), QueryDest.bestMatch(), QueryDest.none()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - // self.assertEqual(0, len(eval_replies)) - // This may not be true for now as : - // - zenoh-c does not check received query properties - // - zenohd does not filter out replies - - z1.query("/test/java/client/**", "", new reply_handler(), QueryDest.none(), QueryDest.bestMatch()); - reps = replies.get(); - // Assert.assertEquals(0, reps.get(0).size()); - // This may not be true for now as : - // - zenoh-c does not check received query properties - // - zenohd does not filter out replies - Assert.assertEquals(2, reps.get(1).size()); - - z2.query("/test/java/client/**", "", new reply_handler()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - Assert.assertEquals(2, reps.get(1).size()); - - z2.query("/test/java/client/**", "", new reply_handler(), QueryDest.bestMatch(), QueryDest.none()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - // self.assertEqual(0, len(eval_replies)) - // This may not be true for now as : - // - zenoh-c does not check received query properties - // - zenohd does not filter out replies - - z2.query("/test/java/client/**", "", new reply_handler(), QueryDest.none(), QueryDest.bestMatch()); - reps = replies.get(); - // Assert.assertEquals(0, reps.get(0).size()); - // This may not be true for now as : - // - zenoh-c does not check received query properties - // - zenohd does not filter out replies - Assert.assertEquals(2, reps.get(1).size()); - - sent_buf = (ByteBuffer) ByteBuffer.allocateDirect(256).put("z2_wr1_spl1".getBytes("UTF-8")).flip(); - sent_res = new Resource("/test/java/client/**", sent_buf, 0, 0); - z2.writeData(sent_res.getRname(), sent_res.getData().duplicate()); - rcvd_res = z1_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z2_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z1_sto1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z2_sto1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - z3_sub1.pull(); - rcvd_res = z3_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - - z1.query("/test/java/client/**", "", new reply_handler()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - Assert.assertEquals(2, reps.get(1).size()); - - z2.query("/test/java/client/**", "", new reply_handler()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - Assert.assertEquals(2, reps.get(1).size()); - - sent_buf = (ByteBuffer) ByteBuffer.allocateDirect(256).put("z1_pub1_spl1".getBytes("UTF-8")).flip(); - sent_res = new Resource("/test/java/client/z1_pub1", sent_buf, 0, 0); - z1_pub1.streamData(sent_res.getData().duplicate()); - rcvd_res = z1_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z2_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z1_sto1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z2_sto1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - z3_sub1.pull(); - rcvd_res = z3_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - - z1.query("/test/java/client/**", "", new reply_handler()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - Assert.assertEquals(2, reps.get(1).size()); - - z2.query("/test/java/client/**", "", new reply_handler()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - Assert.assertEquals(2, reps.get(1).size()); - - sent_buf = (ByteBuffer) ByteBuffer.allocateDirect(256).put("z2_pub1_spl1".getBytes("UTF-8")).flip(); - sent_res = new Resource("/test/java/client/z2_pub1", sent_buf, 0, 0); - z2_pub1.streamData(sent_res.getData().duplicate()); - rcvd_res = z1_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z2_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z1_sto1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - rcvd_res = z2_sto1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - z3_sub1.pull(); - rcvd_res = z3_sub1_last_res.get(); - Assert.assertTrue(resourceEquals(sent_res, rcvd_res)); - - z1.query("/test/java/client/**", "", new reply_handler()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - Assert.assertEquals(2, reps.get(1).size()); - - z2.query("/test/java/client/**", "", new reply_handler()); - reps = replies.get(); - Assert.assertEquals(2, reps.get(0).size()); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(0))); - Assert.assertTrue(resourceEquals(sent_res, reps.get(0).get(1))); - Assert.assertEquals(2, reps.get(1).size()); - - z1_sub1.undeclare(); - z2_sub1.undeclare(); - z3_sub1.undeclare(); - z1_sto1.undeclare(); - z2_sto1.undeclare(); - z1_eval1.undeclare(); - z2_eval1.undeclare(); - z1_pub1.undeclare(); - z2_pub1.undeclare(); - - z1.close(); - z2.close(); - z3.close(); - } -} diff --git a/zenoh/src/it/java/org/eclipse/zenoh/test/SelectorTest.java b/zenoh/src/it/java/org/eclipse/zenoh/test/SelectorTest.java deleted file mode 100644 index 808195f8..00000000 --- a/zenoh/src/it/java/org/eclipse/zenoh/test/SelectorTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.test; - -import org.junit.Assert; -import org.junit.Test; - -import org.eclipse.zenoh.*; - -public class SelectorTest { - - private void testSelector(String s, String expPath, String expFilter, String expFragment) { - try { - Selector sel = new Selector(s); - Assert.assertEquals("Selector for " + s + " has unexpected path: " + sel.getPath(), expPath, sel.getPath()); - Assert.assertEquals("Selector for " + s + " has unexpected predicate: " + sel.getFilter(), expFilter, - sel.getFilter()); - Assert.assertEquals("Selector for " + s + " has unexpected fragment: " + sel.getFragment(), expFragment, - sel.getFragment()); - } catch (Throwable e) { - e.printStackTrace(); - Assert.fail(e.toString()); - } - - } - - @Test - public final void testSelectors() { - testSelector("/a/b/c", "/a/b/c", "", ""); - testSelector("/a/b/c?xyz", "/a/b/c", "xyz", ""); - testSelector("/a/b/c#xyz", "/a/b/c", "", "xyz"); - testSelector("/a/b/c?ghi?xyz", "/a/b/c", "ghi?xyz", ""); - testSelector("/a/b/c#ghi#xyz", "/a/b/c", "", "ghi#xyz"); - testSelector("/a/b/c?ghi#xyz", "/a/b/c", "ghi", "xyz"); - testSelector("/a/b/c#ghi?xyz", "/a/b/c", "", "ghi?xyz"); - testSelector("/*/b/**", "/*/b/**", "", ""); - } - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Admin.java b/zenoh/src/main/java/org/eclipse/zenoh/Admin.java deleted file mode 100644 index 34aefd2e..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Admin.java +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import java.util.Collection; -import java.util.Properties; -import java.util.Map; -import java.util.Hashtable; - -import org.eclipse.zenoh.core.ZException; - -/** - * The zenoh administration class. - */ -public class Admin { - - private Workspace w; - private String zid; - - protected Admin(Workspace w, String zid) { - this.w = w; - this.zid = zid; - } - - private Properties propertiesOfValue(Value v) { - if (v instanceof PropertiesValue) { - return ((PropertiesValue) v).getProperties(); - } else { - Properties p = new Properties(); - p.setProperty("value", v.toString()); - return p; - } - } - - /************* Backends management *************/ - - /** - * Add a backend in the connected zenoh router (i.e. the one you are directly - * connected to). - * - * @param beid the backend identifier. - * @param properties the properties for backend initialization. - * @throws ZException if an error occurs. - */ - public void addBackend(String beid, Properties properties) throws ZException { - addBackend(beid, properties, this.zid); - } - - /** - * Add a backend in the specified zenoh router, not necessarily the one you are - * connected to. - * - * @param beid the backend identifier. - * @param properties the properties for backend initialization. - * @param zid the zenoh router identifier. - * @throws ZException if an error occurs. - */ - public void addBackend(String beid, Properties properties, String zid) throws ZException { - String path = String.format("/@/router/%s/plugin/storages/backend/%s", zid, beid); - w.put(new Path(path), new PropertiesValue(properties)); - } - - /** - * Get a backend's properties from the connected zenoh router (i.e. the one you - * are directly connected to). - * - * @param beid the backend identifier - * @return the backend properties - * @throws ZException if an error occurs. - */ - public Properties getBackend(String beid) throws ZException { - return getBackend(beid, this.zid); - } - - /** - * Get a backend's properties from the specified zenoh router, not necessarily - * the one you are connected to. - * - * @param beid the backend identifier - * @param zid the zenoh router identifier. - * @return the backend properties - * @throws ZException if an error occurs. - */ - public Properties getBackend(String beid, String zid) throws ZException { - String sel = String.format("/@/router/%s/plugin/storages/backend/%s", zid, beid); - Collection entries = w.get(new Selector(sel)); - if (!entries.iterator().hasNext()) { - return null; - } else { - return propertiesOfValue(entries.iterator().next().getValue()); - } - } - - /** - * Get all the backends from the connected zenoh router (i.e. the one you are - * directly connected to). - * - * @return a map of the backends properties, indexed by the backends - * identifiers. - * @throws ZException if an error occurs. - */ - public Map getBackends() throws ZException { - return getBackends(this.zid); - } - - /** - * Get all the backends from the specified zenoh router, not necessarily the one - * you are connected to. - * - * @param zid the zenoh router identifier. - * @return a map of the backends properties, indexed by the backends - * identifiers. - * @throws ZException if an error occurs. - */ - public Map getBackends(String zid) throws ZException { - String sel = String.format("/@/router/%s/plugin/storages/backend/*", zid); - Collection entries = w.get(new Selector(sel)); - Map result = new Hashtable(entries.size()); - for (Data pv : entries) { - String beid = pv.getPath().toString().substring(sel.length() - 1); - result.put(beid, propertiesOfValue(pv.getValue())); - } - return result; - } - - /** - * Remove a backend from the connected zenoh router (i.e. the one you are - * directly connected to). - * - * @param beid the backend identifier. - * @throws ZException if an error occurs. - */ - public void removeBackend(String beid) throws ZException { - removeBackend(beid, this.zid); - } - - /** - * Remove a backend from the specified zenoh router, not necessarily the one you - * are connected to. - * - * @param beid the backend identifier. - * @param zid the zenoh router identifier. - * @throws ZException if an error occurs. - */ - public void removeBackend(String beid, String zid) throws ZException { - String path = String.format("/@/router/%s/plugin/storages/backend/%s", zid, beid); - w.remove(new Path(path)); - } - - /************* Storages management *************/ - - /** - * Add a storage in the connected zenoh router, using an automatically chosen - * backend. - * - * @param stid the storage identifier - * @param properties the properties for storage initialization. - * @throws ZException if an error occurs. - */ - public void addStorage(String stid, Properties properties) throws ZException { - addStorageOnBackend(stid, properties, "auto", this.zid); - } - - /** - * Add a storage in the specified zenoh router, using an automatically chosen - * backend. - * - * @param stid the storage identifier - * @param properties the properties for storage initialization. - * @param zid the zenoh router identifier. - * @throws ZException if an error occurs. - */ - public void addStorage(String stid, Properties properties, String zid) throws ZException { - addStorageOnBackend(stid, properties, "auto", zid); - } - - /** - * Add a storage in the connected zenoh router, using the specified backend. - * - * @param stid the storage identifier. - * @param properties the properties for storage initialization. - * @param backend the identifier of the backend to use for the storage. - * @throws ZException if an error occurs. - */ - public void addStorageOnBackend(String stid, Properties properties, String backend) throws ZException { - addStorageOnBackend(stid, properties, backend, this.zid); - } - - /** - * Add a storage in the specified zenoh router, using the specified backend. - * - * @param stid the storage identifier. - * @param properties the properties for storage initialization. - * @param backend the identifier of the backend to use for the storage. - * @param zid the zenoh router identifier. - * @throws ZException if an error occurs. - */ - public void addStorageOnBackend(String stid, Properties properties, String backend, String zid) throws ZException { - String path = String.format("/@/router/%s/plugin/storages/backend/%s/storage/%s", zid, backend, stid); - w.put(new Path(path), new PropertiesValue(properties)); - } - - /** - * Get a storage's properties from the connected zenoh router. - * - * @param stid the storage identifier - * @return the storage properties - * @throws ZException if an error occurs. - */ - public Properties getStorage(String stid) throws ZException { - return getStorage(stid, this.zid); - } - - /** - * Get a storage's properties from the specified zenoh router. - * - * @param stid the storage identifier - * @param zid the zenoh router identifier. - * @return the storage properties - * @throws ZException if an error occurs. - */ - public Properties getStorage(String stid, String zid) throws ZException { - String sel = String.format("/@/router/%s/plugin/storages/backend/*/storage/%s", zid, stid); - Collection entries = w.get(new Selector(sel)); - if (!entries.iterator().hasNext()) { - return null; - } else { - return propertiesOfValue(entries.iterator().next().getValue()); - } - } - - /** - * Get all the storages from the connected zenoh router. - * - * @return a map of the storages properties, indexed by the storages - * identifiers. - * @throws ZException if an error occurs. - */ - public Map getStorages() throws ZException { - return getStoragesFromBackend("*", this.zid); - } - - /** - * Get all the storages from the specified zenoh router. - * - * @param zid the zenoh router identifier. - * @return a map of the storages properties, indexed by the storages - * identifiers. - * @throws ZException if an error occurs. - */ - public Map getStorages(String zid) throws ZException { - return getStoragesFromBackend("*", zid); - } - - /** - * Get all the storages from the specified backend within the connected zenoh - * router. - * - * @param backend the backend identifier. - * @return a map of the storages properties, indexed by the storages - * identifiers. - * @throws ZException if an error occurs. - */ - public Map getStoragesFromBackend(String backend) throws ZException { - return getStoragesFromBackend(backend, this.zid); - } - - /** - * Get all the storages from the specified backend within the specified zenoh - * router. - * - * @param backend the backend identifier. - * @param zid the zenoh router identifier. - * @return a map of the storages properties, indexed by the storages - * identifiers. - * @throws ZException if an error occurs. - */ - public Map getStoragesFromBackend(String backend, String zid) throws ZException { - String sel = String.format("/@/router/%s/plugin/storages/backend/%s/storage/*", zid, backend); - Collection data = w.get(new Selector(sel)); - Map result = new Hashtable(data.size()); - for (Data d : data) { - String stPath = d.getPath().toString(); - String stid = stPath.substring(stPath.lastIndexOf('/') + 1); - result.put(stid, propertiesOfValue(d.getValue())); - } - return result; - } - - /** - * Remove a storage from the connected zenoh router. - * - * @param stid the storage identifier. - * @throws ZException if an error occurs. - */ - public void removeStorage(String stid) throws ZException { - removeStorage(stid, this.zid); - } - - /** - * Remove a storage from the specified zenoh router. - * - * @param stid the storage identifier. - * @param zid the zenoh router identifier. - * @throws ZException if an error occurs. - */ - public void removeStorage(String stid, String zid) throws ZException { - String sel = String.format("/@/router/%s/plugin/storages/backend/*/storage/%s", zid, stid); - Collection entries = w.get(new Selector(sel)); - if (entries.iterator().hasNext()) { - Path p = entries.iterator().next().getPath(); - w.remove(p); - } - } - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Change.java b/zenoh/src/main/java/org/eclipse/zenoh/Change.java deleted file mode 100644 index da4c45b2..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Change.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import org.eclipse.zenoh.core.Timestamp; -import org.eclipse.zenoh.core.ZException; - -/** - * The notification of a change for a resource in zenoh. See {@link Listener}. - */ -public class Change { - - /** - * The kind of Change: either {@link #PUT}, {@link #UPDATE} or {@link #REMOVE}. - */ - public enum Kind { - PUT(0x00), UPDATE(0x01), REMOVE(0x02); - - private int numVal; - - private Kind(int numVal) { - this.numVal = numVal; - } - - /** - * Returns the numeric value of the change kind (the same than in zenoh-c). - * - * @return the numeric value. - */ - public int value() { - return numVal; - } - - protected static Kind fromInt(int numVal) throws ZException { - if (numVal == PUT.value()) { - return PUT; - } else if (numVal == UPDATE.value()) { - return UPDATE; - } else if (numVal == REMOVE.value()) { - return REMOVE; - } else { - throw new ZException("Unkown change kind value: " + numVal); - } - } - } - - private Path path; - private Kind kind; - private Timestamp timestamp; - private Value value; - - protected Change(Path path, Kind kind, Timestamp timestamp, Value value) { - this.path = path; - this.kind = kind; - this.timestamp = timestamp; - this.value = value; - } - - /** - * Returns the {@link Path} of resource that changed. - * - * @return the resource path. - */ - public Path getPath() { - return path; - } - - /** - * Returns the {@link Kind} of change. - * - * @return the kind of change. - */ - public Kind getKind() { - return kind; - } - - /** - * Returns the {@link Timestamp} when change occured. - * - * @return the timestamp. - */ - public Timestamp getTimestamp() { - return timestamp; - } - - /** - * Depending of the change {@link Kind}, returns: - *
    - *
  • if kind is {@link Kind#PUT}: the new value
  • - *
  • if kind is {@link Kind#UPDATE}: the delta value
  • - *
  • if kind is {@link Kind#REMOVE}: null
  • - *
- * - * @return the new value (complete or delta), or null. - */ - public Value getValue() { - return value; - } - -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Data.java b/zenoh/src/main/java/org/eclipse/zenoh/Data.java deleted file mode 100644 index ae68cb26..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Data.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import org.slf4j.LoggerFactory; - -import org.eclipse.zenoh.core.Timestamp; - -/** - * A zenoh data returned by a {@link Workspace#get(Selector)} query. The Data - * objects are comparable according to their {@link Timestamp}. Note that zenoh - * makes sure that each published path/value has a unique timestamp accross the - * system. - */ -public class Data implements Comparable { - - private Path path; - private Value value; - private Timestamp timestamp; - - protected Data(Path path, Value value, Timestamp timestamp) { - this.path = path; - this.value = value; - this.timestamp = timestamp; - } - - /** - * Returns the {@link Path} of the data. - * - * @return the path of the data. - */ - public Path getPath() { - return path; - } - - /** - * Returns the {@link Value} of the data. - * - * @return the value of the data. - */ - public Value getValue() { - return value; - } - - /** - * Returns the {@link Timestamp} of the data. - * - * @return the timestamp of the data. - */ - public Timestamp getTimestamp() { - return timestamp; - } - - @Override - public int compareTo(Data o) { - // Order entires according to their timestamps - return timestamp.compareTo(o.timestamp); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (!(obj instanceof Data)) - return false; - - Data o = (Data) obj; - // As timestamp is unique per data, only compare timestamps. - if (!timestamp.equals(o.timestamp)) - return false; - - // however, log a warning if path and values are different... - if (!path.equals(o.path)) { - LoggerFactory.getLogger("org.eclipse.zenoh").warn( - "INTERNAL ERROR: 2 entries with same timestamp {} have different paths: {} vs. {}", timestamp, path, - o.path); - } - if (!value.equals(o.value)) { - LoggerFactory.getLogger("org.eclipse.zenoh").warn( - "INTERNAL ERROR: 2 entries with same timestamp {} have different values: {} vs. {}", timestamp, - value, o.value); - } - - return true; - } - - @Override - public int hashCode() { - return timestamp.hashCode(); - } -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Encoding.java b/zenoh/src/main/java/org/eclipse/zenoh/Encoding.java deleted file mode 100644 index 2d9e3a19..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Encoding.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import org.eclipse.zenoh.core.ZException; - -/** - * A description of the {@link Value} format, allowing zenoh to know how to - * encode/decode the value to/from a bytes buffer. - */ -public enum Encoding { - - /** - * The value has a RAW encoding (i.e. it's a bytes buffer). - */ - RAW(RawValue.Decoder.getEncodingFlag(), RawValue.Decoder), - /** - * The value is an UTF-8 string. - */ - STRING(StringValue.Decoder.getEncodingFlag(), StringValue.Decoder), - /** - * The value if a list of keys/values, encoded as an UTF-8 string. The - * keys/values are separated by ';' character, and each key is separated from - * its associated value (if any) with a '=' character. - */ - PROPERTIES(PropertiesValue.Decoder.getEncodingFlag(), PropertiesValue.Decoder), - /** - * The value is a JSON structure in an UTF-8 string. - */ - JSON((short) 0x04, StringValue.Decoder), - - INT(IntValue.Decoder.getEncodingFlag(), IntValue.Decoder), - - FLOAT(FloatValue.Decoder.getEncodingFlag(), FloatValue.Decoder); - - private short flag; - private Value.Decoder decoder; - - private Encoding(short flag, Value.Decoder decoder) { - this.flag = flag; - this.decoder = decoder; - } - - /** - * Returns the numeric flag corresponding to the encoding, for usage with - * zenoh-net. - * - * @return the encoding flag. - */ - public short getFlag() { - return flag; - } - - /** - * Returns the {@link Value.Decoder} for this encoding. - * - * @return the decoder. - */ - public Value.Decoder getDecoder() { - return decoder; - } - - protected static Encoding fromFlag(short flag) throws ZException { - if (flag == RAW.getFlag()) { - return RAW; - } else if (flag == STRING.getFlag()) { - return STRING; - } else if (flag == PROPERTIES.getFlag()) { - return PROPERTIES; - } else if (flag == JSON.getFlag()) { - return JSON; - } else if (flag == INT.getFlag()) { - return INT; - } else if (flag == FLOAT.getFlag()) { - return FLOAT; - } else { - throw new ZException("Unkown encoding flag: " + flag); - } - } - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Eval.java b/zenoh/src/main/java/org/eclipse/zenoh/Eval.java deleted file mode 100644 index 82a5afd3..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Eval.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import java.util.Properties; - -/** - * Interface to be implemented by evaluation functions (see - * {@link Workspace#registerEval(Path, Eval)}) - */ -public interface Eval { - - /** - * The callback operation called each time the {@link Workspace#get(Selector)} - * operation is called for a Selector matching the Path this Eval is registered - * with. Zenoh will wrap the returned {@link Value} into a {@link Data} and add - * it to the result of the get() operation. - * - * @param path the Path with which the Eval has been registered with (in case - * the same Eval is registered with several Paths). - * @param props the Properties specified in the Selector used in eval operation. - * @return a Value resulting of the evaluation. - */ - public Value callback(Path path, Properties props); -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/FloatValue.java b/zenoh/src/main/java/org/eclipse/zenoh/FloatValue.java deleted file mode 100644 index de4d6d26..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/FloatValue.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.eclipse.zenoh; - -import java.nio.ByteBuffer; -import java.nio.charset.Charset; - -/** - * A {@link Value} containing a 64-bits signed float (i.e. a Java double). - */ -public class FloatValue implements Value { - - private static final short ENCODING_FLAG = 0x07; - - private static final Charset utf8 = Charset.forName("UTF-8"); - - private double v; - - /** - * Creates an IntValue containing a int. - * - * @param v the float - */ - public FloatValue(double v) { - this.v = v; - } - - /** - * Returns the string from this StringValue - * - * @return the string - */ - public double getFloat() { - return this.v; - } - - @Override - public Encoding getEncoding() { - return Encoding.FLOAT; - } - - public static ByteBuffer encode(double v) { - return ByteBuffer.wrap(Double.toString(v).getBytes()); - } - - @Override - public ByteBuffer encode() { - return FloatValue.encode(this.v); - } - - @Override - public String toString() { - return Double.toString(this.v); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (!(obj instanceof FloatValue)) - return false; - - return this.v == (((FloatValue) obj).v); - } - - @Override - public int hashCode() { - return this.hashCode(); - } - - /** - * The {@link Value.Decoder} for {@link StringValue}s. - */ - public static final Value.Decoder Decoder = new Value.Decoder() { - @Override - public short getEncodingFlag() { - return ENCODING_FLAG; - } - - @Override - public Value decode(ByteBuffer buf) { - return new FloatValue(Float.parseFloat(new String(buf.array(), utf8))); - } - }; -} - - diff --git a/zenoh/src/main/java/org/eclipse/zenoh/IntValue.java b/zenoh/src/main/java/org/eclipse/zenoh/IntValue.java deleted file mode 100644 index 9b750384..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/IntValue.java +++ /dev/null @@ -1,86 +0,0 @@ -package org.eclipse.zenoh; - -import java.nio.ByteBuffer; -import java.nio.charset.Charset; - -/** - * A {@link Value} containing a 64-bits signed integer (i.e. a Java long). - */ -public class IntValue implements Value { - - private static final short ENCODING_FLAG = 0x06; - - private static final Charset utf8 = Charset.forName("UTF-8"); - - private long v; - - /** - * Creates an IntValue containing a long. - * - * @param v the long - */ - public IntValue(long v) { - this.v = v; - } - - /** - * Returns the string from this StringValue - * - * @return the string - */ - public long getInt() { - return this.v; - } - - @Override - public Encoding getEncoding() { - return Encoding.INT; - } - - public static ByteBuffer encode(long v) { - return ByteBuffer.wrap(Long.toString(v).getBytes()); - } - - @Override - public ByteBuffer encode() { - return IntValue.encode(this.v); - } - - @Override - public String toString() { - return Long.toString(this.v); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (!(obj instanceof IntValue)) - return false; - - return this.v == (((IntValue) obj).v); - } - - @Override - public int hashCode() { - return this.hashCode(); - } - - /** - * The {@link Value.Decoder} for {@link StringValue}s. - */ - public static final Value.Decoder Decoder = new Value.Decoder() { - @Override - public short getEncodingFlag() { - return ENCODING_FLAG; - } - - @Override - public Value decode(ByteBuffer buf) { - return new IntValue(Integer.parseInt(new String(buf.array(), utf8))); - } - }; -} - diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Listener.java b/zenoh/src/main/java/org/eclipse/zenoh/Listener.java deleted file mode 100644 index 92c08a8c..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Listener.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import java.util.List; - -/** - * Interface to be implemented for subscriptions (see - * {@link Workspace#subscribe(Selector, Listener)}) - */ -public interface Listener { - - /** - * The callback operation called for all changes on subscribed paths. - * - * @param changes the list of changes. - */ - public void onChanges(List changes); -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Path.java b/zenoh/src/main/java/org/eclipse/zenoh/Path.java deleted file mode 100644 index 73e46f8a..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Path.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -/** - * A zenoh Path is a set of strings separated by '/' , as in a filesystem path. - * A Path cannot contain any '*' character. Examples of paths: - * "/zenoh/examples/test" , "/com/adlink/building/fr/floor/1/office/2" ... - *

- * A path can be absolute (i.e. starting with a `'/'`) or relative to a - * {@link Workspace}. - * - * @see Workspace#put(Path, Value) - */ -public class Path implements Comparable { - - private String path; - - /** - * Create a Path from a string such as "/zenoh/examples/test". - * - * @param p the string - */ - public Path(String p) { - if (p == null) { - throw new NullPointerException("The given path is null"); - } - if (p.isEmpty()) { - throw new IllegalArgumentException("Invalid path (empty String)"); - } - for (int i = 0; i < p.length(); ++i) { - char c = p.charAt(i); - if (c == '?' || c == '#' || c == '[' || c == ']' || c == '*') - throw new IllegalArgumentException("Invalid path: " + p + " (forbidden character at index " + i + ")"); - } - this.path = removeUselessSlashes(p); - } - - private static String removeUselessSlashes(String s) { - String result = s.replaceAll("/+", "/"); - if (result.charAt(result.length() - 1) == '/') { - return result.substring(0, result.length() - 1); - } else { - return result; - } - } - - @Override - public String toString() { - return path; - } - - @Override - public int compareTo(Path p) { - return this.path.compareTo(p.path); - } - - @Override - public boolean equals(Object object) { - if (object == null) { - return false; - } - if (this == object) { - return true; - } - if (object instanceof Path) { - return this.path.equals(((Path) object).path); - } - return false; - } - - @Override - public int hashCode() { - return path.hashCode(); - } - - /** - * The Path length (i.e. the length of the Path as a string) - * - * @return the length - */ - public int length() { - return path.length(); - } - - static boolean isRelative(String p) { - return p.length() == 0 || p.charAt(0) != '/'; - } - - /** - * Returns true if the Path is relative (i.e. it doesn't start with '/') - * - * @return true if the Path is relative. - */ - public boolean isRelative() { - return length() == 0 || path.charAt(0) != '/'; - } - - /** - * Returns a new Path made of the concatenation of the specified prefix and this - * Path. - * - * @param prefix the prefix to add. - * @return a new Path made of the prefix plus this Path. - */ - public Path addPrefix(Path prefix) { - return new Path(prefix.toString() + "/" + this.path); - } - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/PropertiesValue.java b/zenoh/src/main/java/org/eclipse/zenoh/PropertiesValue.java deleted file mode 100644 index 3a57f196..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/PropertiesValue.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import java.nio.charset.Charset; -import java.nio.ByteBuffer; -import java.util.Map; -import java.util.Properties; - -/** - * A {@link Value} containing {@link Properties}. - */ -public class PropertiesValue implements Value { - - private static final short ENCODING_FLAG = 0x03; - - private static final Charset utf8 = Charset.forName("UTF-8"); - - private Properties p; - - /** - * Creates a PropertiesValue containing some {@link Properties}. - * - * @param p the properties - */ - public PropertiesValue(Properties p) { - this.p = p; - } - - /** - * Returns the properties from this PropertiesValue - * - * @return the properties - */ - public Properties getProperties() { - return p; - } - - @Override - public Encoding getEncoding() { - return Encoding.PROPERTIES; - } - - @Override - public ByteBuffer encode() { - return ByteBuffer.wrap(toString().getBytes(utf8)); - } - - private static final String PROP_SEP = ";"; - private static final String KV_SEP = "="; - - @Override - public String toString() { - StringBuffer buf = new StringBuffer(); - int i = 0; - for (Map.Entry e : p.entrySet()) { - buf.append(e.getKey()).append(KV_SEP).append(e.getValue()); - if (++i < p.size()) { - buf.append(PROP_SEP); - } - } - return buf.toString(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (!(obj instanceof PropertiesValue)) - return false; - - return p.equals(((PropertiesValue) obj).p); - } - - @Override - public int hashCode() { - return p.hashCode(); - } - - /** - * The {@link Value.Decoder} for {@link PropertiesValue}s. - */ - public static final Value.Decoder Decoder = new Value.Decoder() { - - @Override - public short getEncodingFlag() { - return ENCODING_FLAG; - } - - private Properties fromString(String s) { - Properties p = new Properties(); - if (s.length() > 0) { - for (String kv : s.split(PROP_SEP)) { - int i = kv.indexOf(KV_SEP); - if (i < 0) { - p.setProperty(kv, ""); - } else { - p.setProperty(kv.substring(0, i), kv.substring(i + 1)); - } - } - } - return p; - } - - @Override - public Value decode(ByteBuffer buf) { - return new PropertiesValue(fromString(new String(buf.array(), utf8))); - } - }; -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/RawValue.java b/zenoh/src/main/java/org/eclipse/zenoh/RawValue.java deleted file mode 100644 index b5c00523..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/RawValue.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import java.nio.ByteBuffer; - -/** - * A {@link Value} containing a {@link ByteBuffer}. - */ -public class RawValue implements Value { - - private static final short ENCODING_FLAG = 0x00; - - private ByteBuffer buf; - - /** - * Creates a RawValue containing a {@link ByteBuffer}. - * - * @param buf the bytes buffer. - */ - public RawValue(ByteBuffer buf) { - this.buf = buf; - } - - /** - * Returns the {@link ByteBuffer} from this RawValue - * - * @return the bytes buffer. - */ - public ByteBuffer getBuffer() { - return buf; - } - - @Override - public Encoding getEncoding() { - return Encoding.RAW; - } - - @Override - public ByteBuffer encode() { - return buf; - } - - @Override - public String toString() { - return buf.toString(); - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (!(obj instanceof RawValue)) - return false; - - return buf.equals(((RawValue) obj).buf); - } - - @Override - public int hashCode() { - return buf.hashCode(); - } - - /** - * The {@link Value.Decoder} for {@link RawValue}s. - */ - public static final Value.Decoder Decoder = new Value.Decoder() { - @Override - public short getEncodingFlag() { - return ENCODING_FLAG; - } - - @Override - public Value decode(ByteBuffer buf) { - return new RawValue(buf); - } - }; -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Selector.java b/zenoh/src/main/java/org/eclipse/zenoh/Selector.java deleted file mode 100644 index 3b2cc7b6..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Selector.java +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -/** - * A zenoh Selector is a string which is the conjunction of an path expression - * identifying a set of keys and some optional parts allowing to refine the set - * of {@link Path}s and associated {@link Value}s. Structure of a selector: - * - *

- * /s1/s2/../sn?x>1&y<2&..&z=4(p1=v1;p2=v2;..;pn=vn)#a;x;y;..;z
- * |          | |            | |                  |  |        |
- * |-- expr --| |-- filter --| |--- properties ---|  |fragment|
- * 
- * - * where: - *
    - *
  • expr: is a path expression. I.e. a string similar to a - * {@link Path} but with character `'*'` allowed. A single `'*'` matches any set - * of characters in a path, except `'/'`. While `"**"` matches any set of - * characters in a path, including `'/'`. A path expression can be absolute - * (i.e. starting with a `'/'`) or relative to a {@link Workspace}.
  • - * - *
  • filter: a list of predicates separated by `'&'` allowing to - * perform filtering on the {@link Value} associated with the matching keys. - * Each predicate has the form "`field``operator``value`" where: - *
      - *
    • field is the name of a field in the value (is applicable and is - * existing. otherwise the predicate is false) - *
    • operator is one of a comparison operators: `<` , `>` , `<=` , `>=` - * , `=` , `!=` - *
    • value is the the value to compare the field's value with - *
    - *
  • - * - *
  • fragment: a list of fields names allowing to return a sub-part of - * each value. This feature only applies to structured values using a - * "self-describing" encoding, such as JSON or XML. It allows to select only - * some fields within the structure. A new structure with only the selected - * fields will be used in place of the original value.
  • - *
- *

- * NOTE: the filters and fragments are not yet supported in current zenoh - * version. - * - * @see Workspace#get(Selector) - * @see Workspace#subscribe(Selector, Listener) - */ -public final class Selector implements Comparable { - - private static final String REGEX_PATH = "[^\\[\\]?#]+"; - private static final String REGEX_FILTER = "[^\\[\\]\\(\\)#]+"; - private static final String REGEX_PROPERTIES = ".*"; - private static final String REGEX_FRAGMENT = ".*"; - private static final Pattern PATTERN = Pattern.compile(String.format("(%s)(\\?(%s)?(\\((%s)\\))?)?(#(%s))?", - REGEX_PATH, REGEX_FILTER, REGEX_PROPERTIES, REGEX_FRAGMENT)); - - private String path; - private String filter; - private String properties; - private String fragment; - private String optionalPart; - private String toString; - - /** - * Create a Selector from a string such as "/zenoh/examples/**?(name=Bob)". - * - * @param p the string - */ - public Selector(String s) throws IllegalArgumentException { - if (s == null) { - throw new NullPointerException("The given selector is null"); - } - if (s.isEmpty()) { - throw new IllegalArgumentException("Invalid selector (empty String)"); - } - - Matcher m = PATTERN.matcher(s); - if (!m.matches()) { - throw new IllegalArgumentException("Invalid selector (not matching regex)"); - } - String path = m.group(1); - String filter = m.group(3); - if (filter == null) - filter = ""; - String properties = m.group(5); - if (properties == null) - properties = ""; - String fragment = m.group(7); - if (fragment == null) - fragment = ""; - - init(path, filter, properties, fragment); - } - - private Selector(String path, String filter, String properties, String fragment) { - init(path, filter, properties, fragment); - } - - private void init(String path, String filter, String properties, String fragment) { - this.path = path; - this.filter = filter; - this.properties = properties; - this.fragment = fragment; - this.optionalPart = String.format("%s%s%s", filter, (properties.length() > 0 ? "(" + properties + ")" : ""), - (fragment.length() > 0 ? "#" + fragment : "")); - this.toString = path + (optionalPart.length() > 0 ? "?" + optionalPart : ""); - } - - /** - * Return the path expression of this Selector. I.e. the substring before the - * '?' character. - * - * @return the path expression. - */ - public String getPath() { - return path; - } - - /** - * Return the filter expression of this Selector. I.e. the substring after the - * '?' character and before the '(' character (or until end of string). - * - * @return the filter expression or an empty string if not present. - */ - public String getFilter() { - return filter; - } - - /** - * Return the properties expression of this Selector. I.e. the substring - * enclosed between '(' and ')' after the '?' character. - * - * @return the properties expression or an empty string if not present. - */ - public String getProperties() { - return properties; - } - - /** - * Return the fragment expression of this Selector. I.e. the substring after the - * '#' character. - * - * @return the fragment expression or an empty string if not present. - */ - public String getFragment() { - return fragment; - } - - protected String getOptionalPart() { - return optionalPart; - } - - @Override - public String toString() { - return toString; - } - - @Override - public int compareTo(Selector s) { - return this.toString.compareTo(s.toString); - } - - @Override - public boolean equals(Object object) { - if (object == null) { - return false; - } - if (this == object) { - return true; - } - if (object instanceof Selector) { - return this.toString.equals(((Selector) object).toString); - } - return false; - } - - @Override - public int hashCode() { - return toString.hashCode(); - } - - /** - * Returns true if the Selector is relative (i.e. it doesn't start with '/') - * - * @return true if the Selector is relative. - */ - public boolean isRelative() { - return path.length() == 0 || path.charAt(0) != '/'; - } - - /** - * Returns a new Selector made of the concatenation of the specified prefix and - * this Selector. - * - * @param prefix the prefix to add. - * @return a new Selector made of the prefix plus this Selector. - */ - public Selector addPrefix(Path prefix) { - return new Selector(prefix.toString() + this.path, this.filter, this.properties, this.fragment); - } - -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/StringValue.java b/zenoh/src/main/java/org/eclipse/zenoh/StringValue.java deleted file mode 100644 index 8726147c..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/StringValue.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import java.nio.charset.Charset; -import java.nio.ByteBuffer; - -/** - * A {@link Value} containing an UTF-8 {@link String}. - */ -public class StringValue implements Value { - - private static final short ENCODING_FLAG = 0x02; - - private static final Charset utf8 = Charset.forName("UTF-8"); - - private String s; - - /** - * Creates a StringValue containing a {@link String}. - * - * @param s the string - */ - public StringValue(String s) { - this.s = s; - } - - /** - * Returns the string from this StringValue - * - * @return the string - */ - public String getString() { - return s; - } - - @Override - public Encoding getEncoding() { - return Encoding.STRING; - } - - public static ByteBuffer encode(String s) { - return ByteBuffer.wrap(s.getBytes(utf8)); - } - - @Override - public ByteBuffer encode() { - return StringValue.encode(s); - } - - @Override - public String toString() { - return s; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (!(obj instanceof StringValue)) - return false; - - return s.equals(((StringValue) obj).s); - } - - @Override - public int hashCode() { - return s.hashCode(); - } - - /** - * The {@link Value.Decoder} for {@link StringValue}s. - */ - public static final Value.Decoder Decoder = new Value.Decoder() { - @Override - public short getEncodingFlag() { - return ENCODING_FLAG; - } - - @Override - public Value decode(ByteBuffer buf) { - return new StringValue(new String(buf.array(), utf8)); - } - }; - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/SubscriptionId.java b/zenoh/src/main/java/org/eclipse/zenoh/SubscriptionId.java deleted file mode 100644 index abf9f0bb..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/SubscriptionId.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import org.eclipse.zenoh.net.Subscriber; - -/** - * The identifier of a subscription. - * - * @see Workspace#subscribe(Selector, Listener) - * @see Workspace#unsubscribe(SubscriptionId) - */ -public final class SubscriptionId { - - private Subscriber sub; - - protected SubscriptionId(Subscriber sub) { - this.sub = sub; - } - - protected Subscriber getZSubscriber() { - return sub; - } -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Value.java b/zenoh/src/main/java/org/eclipse/zenoh/Value.java deleted file mode 100644 index 190cd7de..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Value.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import java.nio.ByteBuffer; - -/** - * Interface of a Value that, associated to a {@link Path}, can be published - * into zenoh via {@link Workspace#put(Path, Value)}, or retrieved via - * {@link Workspace#get(Selector)} or via a subscription - * ({@link Workspace#subscribe(Selector, Listener)}). - */ -public interface Value { - - /** - * Interface of a Value decoder, able to transform a {@link ByteBuffer} into a - * Value. - */ - public interface Decoder { - /** - * Returns the flag of the {@link Encoding} that this Decoder supports. (@see - * Encoding#getFlag()). - * - * @return the encoding flag. - */ - public short getEncodingFlag(); - - /** - * Decode a Value that is encoded in a {@link ByteBuffer}. - * - * @param buf the ByteBuffer containing the encoded Value. - * @return the Value. - */ - public Value decode(ByteBuffer buf); - } - - /** - * Return the {@link Encoding} of this Value. - * - * @return the encoding. - */ - public Encoding getEncoding(); - - /** - * Returns a new {@link ByteBuffer} containing the Value encoded as a sequence - * of bytes. - * - * @return the encoded Value. - */ - public ByteBuffer encode(); -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Workspace.java b/zenoh/src/main/java/org/eclipse/zenoh/Workspace.java deleted file mode 100644 index 3ac2ed11..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Workspace.java +++ /dev/null @@ -1,494 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import org.eclipse.zenoh.core.Timestamp; -import org.eclipse.zenoh.core.ZException; -import org.eclipse.zenoh.net.*; - -import java.util.Collection; -import java.util.List; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.Map; -import java.util.Hashtable; -import java.util.Properties; -import java.util.SortedSet; -import java.util.TreeSet; -import java.util.concurrent.ExecutorService; -import java.nio.ByteBuffer; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * A Workspace to operate on Zenoh. - */ -public class Workspace { - - private static final Logger LOG = LoggerFactory.getLogger("org.eclipse.zenoh"); - - private static final short KIND_PUT = (short) 0x0; - private static final short KIND_UPDATE = (short) 0x1; - private static final short KIND_REMOVE = (short) 0x2; - private static final ByteBuffer EMPTY_BUF = ByteBuffer.allocateDirect(0); - - private Path path; - private Session session; - private ExecutorService threadPool; - private Map evals; - - - protected Workspace(Path path, Session session, ExecutorService threadPool) { - this.path = path; - this.session = session; - this.threadPool = threadPool; - this.evals = new Hashtable(); - } - - private Path toAsbsolutePath(Path p) { - if (p.isRelative()) { - return p.addPrefix(this.path); - } else { - return p; - } - } - - private Selector toAsbsoluteSelector(Selector s) { - if (s.isRelative()) { - return s.addPrefix(this.path); - } else { - return s; - } - } - - /** - * Put a path/value into Zenoh. - * - * @param path the {@link Path}. Can be absolute or relative to the workspace. - * @param value the {@link Value}. - * @throws ZException if put failed. - */ - public void put(Path path, Value value) throws ZException { - path = toAsbsolutePath(path); - LOG.debug("Put on {} : {}", path, value); - try { - ByteBuffer data = value.encode(); - session.writeData(path.toString(), data, value.getEncoding().getFlag(), KIND_PUT); - } catch (ZException e) { - throw new ZException("Put on " + path + " failed", e); - } - } - - /** - * Put a path/String into Zenoh. - * - * @param path the {@link Path}. Can be absolute or relative to the workspace. - * @param value the value {@link java.lang.String}. - * @throws ZException if put failed. - */ - public void put(Path path, String value) throws ZException { - path = toAsbsolutePath(path); - LOG.debug("Put string on {} : {}", path, value); - try { - this.session.writeData(path.toString(), StringValue.encode(value), Encoding.STRING.getFlag(), KIND_PUT); - } catch (ZException e) { - throw new ZException("Put on " + path + " failed", e); - } - } - - /** - * Put a path/integer into Zenoh. - * - * @param path the {@link Path}. Can be absolute or relative to the workspace. - * @param value the integer value as a long - * @throws ZException if put failed. - */ - public void put(Path path, long value) throws ZException { - path = toAsbsolutePath(path); - LOG.debug("Put int on {} : {}", path, value); - try { - this.session.writeData(path.toString(), IntValue.encode(value), Encoding.INT.getFlag(), KIND_PUT); - } catch (ZException e) { - throw new ZException("Put on " + path + " failed", e); - } - } - - /** - * Put a path/float into Zenoh. - * - * @param path the {@link Path}. Can be absolute or relative to the workspace. - * @param value the float value as a double. - * @throws ZException if put failed. - */ - public void put(Path path, double value) throws ZException { - path = toAsbsolutePath(path); - LOG.debug("Put float on {} : {}", path, value); - try { - this.session.writeData(path.toString(), FloatValue.encode(value), Encoding.FLOAT.getFlag(), KIND_PUT); - } catch (ZException e) { - throw new ZException("Put on " + path + " failed", e); - } - } - - /** - * Put a path/ByteBuffer into Zenoh using the RAW encoding. - * - * @param path the {@link Path}. Can be absolute or relative to the workspace. - * @param value the ByteBuffer. - * @throws ZException if put failed. - */ - public void put(Path path, ByteBuffer value) throws ZException { - path = toAsbsolutePath(path); - LOG.debug("Put float on {} : {}", path, value); - try { - this.session.writeData(path.toString(), value, Encoding.RAW.getFlag(), KIND_PUT); - } catch (ZException e) { - throw new ZException("Put on " + path + " failed", e); - } - } - - /** - * Update a path/value into Zenoh. - * - * @param path the {@link Path}. Can be absolute or relative to the workspace. - * @param value a delta to be applied on the existing value. - * @throws ZException if update failed. - */ - public void update(Path path, Value value) throws ZException { - path = toAsbsolutePath(path); - LOG.debug("Update on {} of {}", path, value); - try { - ByteBuffer data = value.encode(); - session.writeData(path.toString(), data, value.getEncoding().getFlag(), KIND_UPDATE); - } catch (ZException e) { - throw new ZException("Update on " + path + " failed", e); - } - } - - /** - * Remove a path/value from Zenoh. - * - * @param path the {@link Path} to be removed. Can be absolute or relative to - * the workspace. - * @throws ZException if remove failed. - */ - public void remove(Path path) throws ZException { - path = toAsbsolutePath(path); - LOG.debug("Remove on {}", path); - try { - session.writeData(path.toString(), EMPTY_BUF, (short) 0, KIND_REMOVE); - } catch (ZException e) { - throw new ZException("Remove on " + path + " failed", e); - } - } - - // Timestamp generated locally when not available in received Data - // @TODO: remove this when we're sure Data always come with a Timestamp. - private static class LocalTimestamp extends Timestamp { - private static final byte[] zeroId = { 0x00 }; - - LocalTimestamp() { - super(currentTime(), zeroId); - } - - private static long currentTime() { - long now = System.currentTimeMillis(); - long sec = (now / 1000) << 32; - float frac = (((float) (now % 1000)) / 1000) * 0x100000000L; - return sec + (long) frac; - } - } - - /** - * Get a selection of path/value from Zenoh. - * - * @param selector the {@link Selector} expressing the selection. - * @return a collection of path/value. - * @throws ZException if get failed. - */ - public Collection get(Selector selector) throws ZException { - final Selector s = toAsbsoluteSelector(selector); - LOG.debug("Get on {}", s); - try { - final Map> map = new Hashtable>(); - final java.util.concurrent.atomic.AtomicBoolean queryFinished = new java.util.concurrent.atomic.AtomicBoolean( - false); - - session.query(s.getPath(), s.getOptionalPart(), new ReplyHandler() { - public void handleReply(ReplyValue reply) { - switch (reply.getKind()) { - case ZN_STORAGE_DATA: - case ZN_EVAL_DATA: - Path path = new Path(reply.getRname()); - ByteBuffer data = reply.getData(); - short encodingFlag = (short) reply.getInfo().getEncoding(); - if (reply.getKind() == ReplyValue.Kind.ZN_STORAGE_DATA) { - LOG.debug("Get on {} => ZN_STORAGE_DATA {} : {} ({} bytes)", s, path, - reply.getInfo().getTimestamp(), data.remaining()); - } else { - LOG.debug("Get on {} => ZN_EVAL_DATA {} : {} ({} bytes)", s, path, - reply.getInfo().getTimestamp(), data.remaining()); - } - try { - Value value = Encoding.fromFlag(encodingFlag).getDecoder().decode(data); - Timestamp ts = reply.getInfo().getTimestamp(); - // @TODO: remove this when we're sure Data always come with a Timestamp. - if (ts == null) { - LOG.warn( - "Get on {}: received data for {} without timestamp. Adding it with current date.", - s, path); - ts = new LocalTimestamp(); - } - Data d = new Data(path, value, ts); - if (!map.containsKey(path)) { - map.put(path, new TreeSet()); - } - map.get(path).add(d); - } catch (ZException e) { - LOG.warn("Get on {}: error decoding reply {} : {}", s, reply.getRname(), e); - } - break; - case ZN_STORAGE_FINAL: - LOG.trace("Get on {} => ZN_STORAGE_FINAL", s); - break; - case ZN_EVAL_FINAL: - LOG.trace("Get on {} => ZN_EVAL_FINAL", s); - break; - case ZN_REPLY_FINAL: - LOG.trace("Get on {} => ZN_REPLY_FINAL", s); - synchronized (map) { - queryFinished.set(true); - map.notify(); - } - break; - } - } - }); - - synchronized (map) { - while (!queryFinished.get()) { - try { - map.wait(); - } catch (InterruptedException e) { - // ignore - } - } - } - - Collection results = new LinkedList(); - - if (isSelectorForSeries(selector)) { - // return all entries - for (SortedSet l : map.values()) { - for (Data e : l) { - results.add(e); - } - } - } else { - // return only the latest data for each path - for (SortedSet l : map.values()) { - results.add(l.last()); - } - } - - return results; - - } catch (ZException e) { - throw new ZException("Get on " + selector + " failed", e); - } - } - - private boolean isSelectorForSeries(Selector sel) { - // search for starttime or stoptime property in selector - String[] props = sel.getProperties().split(";"); - for (String p : props) { - if (p.startsWith("starttime") || p.startsWith("stoptime")) { - return true; - } - } - return false; - } - - /** - * Subscribe to a selection of path/value from Zenoh. - * - * @param selector the {@link Selector} expressing the selection. - * @param listener the {@link Listener} that will be called for each change of a - * path/value matching the selection. - * @return a {@link SubscriptionId}. - * @throws ZException if subscribe failed. - */ - public SubscriptionId subscribe(Selector selector, Listener listener) throws ZException { - final Selector s = toAsbsoluteSelector(selector); - LOG.debug("subscribe on {}", selector); - try { - Subscriber sub = session.declareSubscriber(s.getPath(), SubMode.push(), new DataHandler() { - public void handleData(String rname, ByteBuffer data, DataInfo info) { - LOG.debug("subscribe on {} : received notif for {} (kind:{})", s, rname, info.getKind()); - // TODO: list of more than 1 change when available in zenoh-c - List changes = new ArrayList(1); - - try { - Path path = new Path(rname); - Change.Kind kind = Change.Kind.fromInt(info.getKind()); - short encodingFlag = (short) info.getEncoding(); - Timestamp timestamp = info.getTimestamp(); - Value value = null; - if (kind != Change.Kind.REMOVE) { - value = Encoding.fromFlag(encodingFlag).getDecoder().decode(data); - } - Change change = new Change(path, kind, timestamp, value); - changes.add(change); - } catch (ZException e) { - LOG.warn("subscribe on {}: error decoding change for {} : {}", s, rname, e); - } - - if (threadPool != null) { - threadPool.execute(new Runnable() { - @Override - public void run() { - try { - listener.onChanges(changes); - } catch (Throwable e) { - LOG.warn("subscribe on {} : error receiving notification for {} : {}", s, rname, e); - LOG.debug("Stack trace: ", e); - } - } - }); - } else { - try { - listener.onChanges(changes); - } catch (Throwable e) { - LOG.warn("subscribe on {} : error receiving notification for {} : {}", s, rname, e); - LOG.debug("Stack trace: ", e); - } - } - } - }); - return new SubscriptionId(sub); - - } catch (ZException e) { - throw new ZException("Subscribe on " + selector + " failed", e); - } - } - - /** - * Unregisters a previous subscription. - * - * @param subid the {@link SubscriptionId} to unregister - * @throws ZException if unsusbscribe failed. - */ - public void unsubscribe(SubscriptionId subid) throws ZException { - try { - subid.getZSubscriber().undeclare(); - } catch (ZException e) { - throw new ZException("Unsubscribe failed", e); - } - } - - private static final Resource[] EMPTY_EVAL_REPLY = new Resource[0]; - - /** - * Registers an evaluation function under the provided {@link Path}. The - * function will be evaluated in a dedicated thread, and thus may call any other - * Workspace operation. - * - * @param path the {@link Path} where the function can be triggered using - * {@link #get(Selector)} - * @param eval the evaluation function - * @throws ZException if registration failed. - */ - public void registerEval(Path path, Eval eval) throws ZException { - final Path p = toAsbsolutePath(path); - LOG.debug("registerEval on {}", p); - try { - QueryHandler qh = new QueryHandler() { - public void handleQuery(String rname, String predicate, RepliesSender repliesSender) { - LOG.debug("Registered eval on {} handling query {}?{}", p, rname, predicate); - Selector s = new Selector(rname + "?" + predicate); - if (threadPool != null) { - threadPool.execute(new Runnable() { - @Override - public void run() { - try { - Value v = eval.callback(p, predicateToProperties(s.getProperties())); - LOG.debug("Registered eval on {} handling query {}?{} returns: {}", p, rname, - predicate, v); - repliesSender.sendReplies(new Resource[] { new Resource(p.toString(), v.encode(), - v.getEncoding().getFlag(), Change.Kind.PUT.value()) }); - } catch (Throwable e) { - LOG.warn( - "Registered eval on {} caught an exception while handling query {} {} : {}", - p, rname, predicate, e); - LOG.debug("Stack trace: ", e); - repliesSender.sendReplies(EMPTY_EVAL_REPLY); - } - } - }); - } else { - try { - Value v = eval.callback(p, predicateToProperties(s.getProperties())); - LOG.debug("Registered eval on {} handling query {}?{} returns: {}", p, rname, predicate, v); - repliesSender.sendReplies(new Resource[] { new Resource(p.toString(), v.encode(), - v.getEncoding().getFlag(), Change.Kind.PUT.value()) }); - } catch (Throwable e) { - LOG.warn("Registered eval on {} caught an exception while handling query {} {} : {}", p, - rname, predicate, e); - LOG.debug("Stack trace: ", e); - repliesSender.sendReplies(EMPTY_EVAL_REPLY); - } - } - } - }; - - org.eclipse.zenoh.net.Eval e = session.declareEval(p.toString(), qh); - evals.put(p, e); - - } catch (ZException e) { - throw new ZException("registerEval on " + p + " failed", e); - } - - } - - /** - * Unregister a previously registered evaluation function. - * - * @param path the {@link Path} where the function has been registered - * @throws ZException if unregistration failed. - */ - public void unregisterEval(Path path) throws ZException { - org.eclipse.zenoh.net.Eval e = evals.remove(path); - if (e != null) { - try { - e.undeclare(); - } catch (ZException ex) { - throw new ZException("unregisterEval failed", ex); - } - } - } - - private static Properties predicateToProperties(String predicate) { - Properties result = new Properties(); - String[] kvs = predicate.split(";"); - for (String kv : kvs) { - int i = kv.indexOf('='); - if (i > 0) { - result.setProperty(kv.substring(0, i), kv.substring(i + 1)); - } - } - return result; - } - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/Zenoh.java b/zenoh/src/main/java/org/eclipse/zenoh/Zenoh.java deleted file mode 100644 index 544c5450..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/Zenoh.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh; - -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.eclipse.zenoh.core.ZException; -import org.eclipse.zenoh.net.Session; -import org.eclipse.zenoh.net.ZNet; - -/** - * The Zenoh client API. - */ -public class Zenoh { - - private static final String PROP_USER = "user"; - private static final String PROP_PASSWORD = "password"; - - private static final Logger LOG = LoggerFactory.getLogger("org.eclipse.zenoh"); - private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); - private static final Charset UTF8 = Charset.forName("UTF-8"); - - private static String hexdump(byte[] bytes) { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < bytes.length; ++i) { - sb.append(HEX_DIGITS[(bytes[i] & 0xF0) >>> 4]).append(HEX_DIGITS[bytes[i] & 0x0F]); - } - return sb.toString(); - } - - private Session session; - private ExecutorService threadPool; - private Admin admin; - - private Zenoh(Session session, String zid) { - this.session = session; - this.threadPool = Executors.newCachedThreadPool(); - Workspace adminWs = new Workspace(new Path("/@"), session, threadPool); - this.admin = new Admin(adminWs, zid); - } - - /** - * Establish a zenoh session through dynamic discovered peers/routers/brokers. - * - * @return a {@link Zenoh} object. - * @throws ZException if login fails. - */ - public static Zenoh login() throws ZException { - return Zenoh.login(null, null); - } - - /** - * Establish a zenoh session via a provided locator. Locator is a string - * representing the network endpoint to which establish the session. If the - * provided locator is null, login will perform some dynamic discovery - * and try to establish the session automatically. When not null, the - * locator must have the format: {@code tcp/:} (for instance - * {@code tcp/127.0.0.1:7447}). - * - * @param locator the locator or null. - * @return a {@link Zenoh} object. - * @throws ZException if login fails. - */ - public static Zenoh login(String locator) throws ZException { - return Zenoh.login(locator, null); - } - - /** - * Establish a zenoh session through dynamic discovered peers/routers/brokers. - * - * @param properties the Properties to be used for this session (e.g. "user", - * "password"...). Can be null. - * @return a {@link Zenoh} object. - * @throws ZException if login fails. - */ - public static Zenoh login(Properties properties) throws ZException { - return Zenoh.login(null, properties); - } - /** - * Establish a zenoh session via a provided locator. Locator is a string - * representing the network endpoint to which establish the session. If the - * provided locator is null, login will perform some dynamic discovery - * and try to establish the session automatically. When not null, the - * locator must have the format: {@code tcp/:} (for instance - * {@code tcp/127.0.0.1:7447}). - * - * @param locator the locator or null. - * @param properties the Properties to be used for this session (e.g. "user", - * "password"...). Can be null. - * @return a {@link Zenoh} object. - * @throws ZException if login fails. - */ - public static Zenoh login(String locator, Properties properties) throws ZException { - try { - LOG.debug("Establishing session to Zenoh router {}", locator); - Session s; - if (properties == null) { - s = Session.open(locator); - } else { - s = Session.open(locator, getSessionProperties(properties)); - } - - Map props = s.info(); - byte[] buf = props.get(ZNet.INFO_PEER_PID_KEY); - if (buf == null) { - throw new ZException("Failed to retrieve Zenoh id from Session info"); - } - String zid = hexdump(buf); - LOG.info("Session established with Zenoh router {}", zid); - return new Zenoh(s, zid); - - } catch (ZException e) { - LOG.warn("Failed to establish session to {}", locator, e); - throw new ZException("Login failed to " + locator, e); - } - } - - private static Map getSessionProperties(Properties properties) { - Map zprops = new HashMap(); - - if (properties.containsKey(PROP_USER)) - zprops.put(ZNet.USER_KEY, properties.getProperty(PROP_USER).getBytes(UTF8)); - if (properties.containsKey(PROP_PASSWORD)) - zprops.put(ZNet.PASSWD_KEY, properties.getProperty(PROP_PASSWORD).getBytes(UTF8)); - - return zprops; - } - - /** - * Terminates the Zenoh session. - */ - public void logout() throws ZException { - threadPool.shutdown(); - try { - session.close(); - this.session = null; - } catch (ZException e) { - throw new ZException("Error during logout", e); - } - } - - /** - * Creates a Workspace on the root path. All relative {@link Selector} or - * {@link Path} used with this Workspace will be relative to this path. - *

- * Notice that all subscription listeners and eval callbacks declared in this - * workspace will be executed by the I/O thread. This implies that no long - * operations or other call to Zenoh shall be performed in those callbacks. - * - * @return a {@link Workspace}. - */ - public Workspace workspace() { - return this.workspace(new Path("/")); - } - - /** - * Creates a Workspace using the provided path. All relative {@link Selector} or - * {@link Path} used with this Workspace will be relative to this path. - *

- * Notice that all subscription listeners and eval callbacks declared in this - * workspace will be executed by the I/O thread. This implies that no long - * operations or other call to Zenoh shall be performed in those callbacks. - * - * @param path the Workspace's path. - * @return a {@link Workspace}. - */ - public Workspace workspace(Path path) { - return new Workspace(path, session, null); - } - - /** - * Creates a Workspace using the provided path. All relative {@link Selector} or - * {@link Path} used with this Workspace will be relative to this path. - *

- * Notice that all subscription listeners and eval callbacks declared in this - * workspace will be executed by a CachedThreadPool. This is useful when - * listeners and/or callbacks need to perform long operations or need to call - * other Zenoh operations. - * - * @param path the Workspace's path. - * @return a {@link Workspace}. - */ - public Workspace workspaceWithExecutor(Path path) { - return new Workspace(path, session, threadPool); - } - - /** - * Creates a Workspace using the provided path. All relative {@link Selector} or - * {@link Path} used with this Workspace will be relative to this path. - *

- * Notice that all subscription listeners and eval callbacks declared in this - * workspace will be executed by a CachedThreadPool. This is useful when - * listeners and/or callbacks need to perform long operations or need to call - * other Zenoh operations. - * - * @return a {@link Workspace}. - */ - public Workspace workspaceWithExecutor() { - return new Workspace(new Path("/"), session, threadPool); - } - /** - * Returns the {@link Admin} object that provides helper operations to - * administer Zenoh. - */ - public Admin admin() { - return admin; - } - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/core/Timestamp.java b/zenoh/src/main/java/org/eclipse/zenoh/core/Timestamp.java deleted file mode 100644 index 8331ab13..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/core/Timestamp.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.core; - -import java.time.Instant; -import java.util.Arrays; - -/** - * Data structure representing a unique timestamp. - */ -public class Timestamp implements Comparable { - - private long time; - private byte[] clockId; - private String asString; - - protected Timestamp(long time, byte[] clockId) { - this.time = time; - this.clockId = clockId; - this.asString = null; - } - - /** - * Return the time as a 64-bit long, where: - *

    - *
  • The higher 32-bit represent the number of seconds since midnight, January - * 1, 1970 UTC - *
  • The lower 32-bit represent a fraction of 1 second. - *
- *

- * WARNING: this time cannot be used with {@link java.util.Date#Date(long)}. - * Rather use {@link #getTimeAsInstant()}. - * - * @return the time as a 64-bits NTP time. - */ - public long getTime() { - return time; - } - - /** - * @return the unique identifier of the clock that created this Timestamp. - */ - public byte[] getClockId() { - return clockId; - } - - /* number of NTP fraction per second (2^32) */ - private static final long FRAC_PER_SEC = 0x100000000L; - - /* number of nanoseconds per second (10^9) */ - private static final long NANO_PER_SEC = 1000000000L; - - /** - * - * @return the Timestamp's creation time as a {@link java.time.Instant}. - */ - public Instant getTimeAsInstant() { - Instant i = Instant.ofEpochSecond(time >> 32); - long frac = time & 0xffffffffL; - return i.plusNanos((frac * NANO_PER_SEC) / FRAC_PER_SEC); - } - - @Override - public int compareTo(Timestamp o) { - int tcmp = Long.compare(this.time, o.time); - if (tcmp != 0) { - return tcmp; - } - - for (int i = 0; i < this.clockId.length; i++) { - int bcmp = Byte.compare(this.clockId[i], o.clockId[i]); - if (bcmp != 0) { - return bcmp; - } - } - - return 0; - } - - @Override - public boolean equals(Object obj) { - if (obj == null) - return false; - if (this == obj) - return true; - if (!(obj instanceof Timestamp)) - return false; - - Timestamp o = (Timestamp) obj; - return this.time == o.time && Arrays.equals(this.clockId, o.clockId); - } - - @Override - public int hashCode() { - int h = Long.hashCode(time); - for (byte b : clockId) - h = 31 * h + b; - - return h; - } - - private static final char[] HEX_DIGITS = "0123456789abcdef".toCharArray(); - - @Override - public String toString() { - synchronized (this) { - if (asString == null) { - StringBuffer sb = new StringBuffer(); - sb.append(getTimeAsInstant().toString()).append('/'); - for (byte b : clockId) { - sb.append(HEX_DIGITS[(b & 0xF0) >> 4]).append(HEX_DIGITS[b & 0x0F]); - } - asString = sb.toString(); - } - } - return asString; - } - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/core/ZException.java b/zenoh/src/main/java/org/eclipse/zenoh/core/ZException.java deleted file mode 100644 index 36e3c6f9..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/core/ZException.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.core; - -import org.eclipse.zenoh.swig.zenohcConstants; -import java.util.HashMap; - -/** - * An Exception raised by Zenoh. - */ -public class ZException extends Exception { - - public static final Integer Z_NO_ERROR_CODE = 0x00; - - public static final Integer Z_VLE_PARSE_ERROR = zenohcConstants.Z_VLE_PARSE_ERROR; - public static final Integer Z_ARRAY_PARSE_ERROR = zenohcConstants.Z_ARRAY_PARSE_ERROR; - public static final Integer Z_STRING_PARSE_ERROR = zenohcConstants.Z_STRING_PARSE_ERROR; - - public static final Integer ZN_PROPERTY_PARSE_ERROR = zenohcConstants.ZN_PROPERTY_PARSE_ERROR; - public static final Integer ZN_PROPERTIES_PARSE_ERROR = zenohcConstants.ZN_PROPERTIES_PARSE_ERROR; - public static final Integer ZN_MESSAGE_PARSE_ERROR = zenohcConstants.ZN_MESSAGE_PARSE_ERROR; - public static final Integer ZN_INSUFFICIENT_IOBUF_SIZE = zenohcConstants.ZN_INSUFFICIENT_IOBUF_SIZE; - public static final Integer ZN_IO_ERROR = zenohcConstants.ZN_IO_ERROR; - public static final Integer ZN_RESOURCE_DECL_ERROR = zenohcConstants.ZN_RESOURCE_DECL_ERROR; - public static final Integer ZN_PAYLOAD_HEADER_PARSE_ERROR = zenohcConstants.ZN_PAYLOAD_HEADER_PARSE_ERROR; - public static final Integer ZN_TX_CONNECTION_ERROR = zenohcConstants.ZN_TX_CONNECTION_ERROR; - public static final Integer ZN_INVALID_ADDRESS_ERROR = zenohcConstants.ZN_INVALID_ADDRESS_ERROR; - public static final Integer ZN_FAILED_TO_OPEN_SESSION = zenohcConstants.ZN_FAILED_TO_OPEN_SESSION; - public static final Integer ZN_UNEXPECTED_MESSAGE = zenohcConstants.ZN_UNEXPECTED_MESSAGE; - - private static final java.util.Map errorCodeToString = new HashMap(); - - static { - errorCodeToString.put(Z_ARRAY_PARSE_ERROR, "Z_ARRAY_PARSE_ERROR"); - errorCodeToString.put(Z_STRING_PARSE_ERROR, "Z_STRING_PARSE_ERROR"); - errorCodeToString.put(ZN_PROPERTY_PARSE_ERROR, "ZN_PROPERTY_PARSE_ERROR"); - errorCodeToString.put(ZN_PROPERTIES_PARSE_ERROR, "ZN_PROPERTIES_PARSE_ERROR"); - errorCodeToString.put(ZN_MESSAGE_PARSE_ERROR, "ZN_MESSAGE_PARSE_ERROR"); - errorCodeToString.put(ZN_INSUFFICIENT_IOBUF_SIZE, "ZN_INSUFFICIENT_IOBUF_SIZE"); - errorCodeToString.put(ZN_IO_ERROR, "ZN_IO_ERROR"); - errorCodeToString.put(ZN_RESOURCE_DECL_ERROR, "ZN_RESOURCE_DECL_ERROR"); - errorCodeToString.put(ZN_PAYLOAD_HEADER_PARSE_ERROR, "ZN_PAYLOAD_HEADER_PARSE_ERROR"); - errorCodeToString.put(ZN_TX_CONNECTION_ERROR, "ZN_TX_CONNECTION_ERROR"); - errorCodeToString.put(ZN_INVALID_ADDRESS_ERROR, "ZN_INVALID_ADDRESS_ERROR"); - errorCodeToString.put(ZN_FAILED_TO_OPEN_SESSION, "ZN_FAILED_TO_OPEN_SESSION"); - errorCodeToString.put(ZN_UNEXPECTED_MESSAGE, "ZN_UNEXPECTED_MESSAGE"); - } - - private int errorCode; - - private static final long serialVersionUID = 402535504102337839L; - - public ZException(String message) { - this(message, Z_NO_ERROR_CODE); - } - - public ZException(String message, Throwable cause) { - this(message, Z_NO_ERROR_CODE, cause); - } - - public ZException(String message, int errorCode) { - super(message); - this.errorCode = errorCode; - } - - public ZException(String message, int errorCode, Throwable cause) { - super(message, cause); - this.errorCode = errorCode; - } - - public int getErrorCode() { - return errorCode; - } - - public String getErrorCodeName() { - String name = errorCodeToString.get(errorCode); - return (name != null ? name : "UNKOWN_ERROR_CODE(" + errorCode + ")"); - } - - public String toString() { - if (errorCode != Z_NO_ERROR_CODE) { - return super.toString() + " (error code:" + getErrorCodeName() + ")"; - } else { - return super.toString(); - } - } -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/DataHandler.java b/zenoh/src/main/java/org/eclipse/zenoh/net/DataHandler.java deleted file mode 100644 index 7bbc82c4..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/DataHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import java.nio.ByteBuffer; - -/** - * A callback interface to be implemented for the reception of subscribed/stored - * resources. See - * {@link Session#declareSubscriber(String, SubMode, DataHandler)} and - * {@link Session#declareStorage(String, StorageHandler)}. - */ -public interface DataHandler { - - /** - * The method that will be called on reception of data matching the subscribed - * or stored resource. - * - * @param rname the resource name of the received data. - * @param data the received data. - * @param info the {@link DataInfo} associated with the received data. - */ - public void handleData(String rname, ByteBuffer data, DataInfo info); - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/DataInfo.java b/zenoh/src/main/java/org/eclipse/zenoh/net/DataInfo.java deleted file mode 100644 index 272cd021..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/DataInfo.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import org.eclipse.zenoh.core.Timestamp; - -/** - * Data structure containing meta informations about the associated data. - */ -public class DataInfo { - - private Timestamp tstamp = null; - private int encoding = 0; - private int kind = 0; - - private static final long ZN_T_STAMP = 0x10; - private static final long ZN_KIND = 0x20; - private static final long ZN_ENCODING = 0x40; - - protected DataInfo(long flags, Timestamp tstamp, int encoding, int kind) { - if ((flags & ZN_T_STAMP) != 0L) { - this.tstamp = tstamp; - } - if ((flags & ZN_KIND) != 0L) { - this.kind = kind; - } - if ((flags & ZN_ENCODING) != 0L) { - this.encoding = encoding; - } - } - - /** - * @return the encoding of the data. - */ - public int getEncoding() { - return encoding; - } - - /** - * @return the unique timestamp at which the data has been produced. - */ - public Timestamp getTimestamp() { - return tstamp; - } - - /** - * @return the kind of the data. - */ - public int getKind() { - return kind; - } -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/Eval.java b/zenoh/src/main/java/org/eclipse/zenoh/net/Eval.java deleted file mode 100644 index 8ac31807..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/Eval.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import org.eclipse.zenoh.core.ZException; -import org.eclipse.zenoh.swig.zenohc; -import org.eclipse.zenoh.swig.zn_eva_t; - -/** - * An Eval (see {@link Session#declareEval(String, EvalCallback)}). - */ -public class Eval { - - private zn_eva_t eval; - - protected Eval(zn_eva_t eval) { - this.eval = eval; - } - - /** - * Undeclare the Eval. - * - * @throws ZException if undeclaration failed. - */ - public void undeclare() throws ZException { - int error = zenohc.zn_undeclare_eval(eval); - if (error != 0) { - throw new ZException("zn_undeclare_eval failed ", error); - } - } - -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/Publisher.java b/zenoh/src/main/java/org/eclipse/zenoh/net/Publisher.java deleted file mode 100644 index 1cffc360..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/Publisher.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import java.nio.ByteBuffer; - -import org.eclipse.zenoh.core.ZException; -import org.eclipse.zenoh.swig.zn_pub_t; -import org.eclipse.zenoh.swig.zenohc; - -/** - * A Publisher (see {@link Session#declarePublisher(String)}). - */ -public class Publisher { - - private zn_pub_t pub; - - protected Publisher(zn_pub_t pub) { - this.pub = pub; - } - - /** - * Undeclare the Publisher. - * - * @throws ZException if undeclaration failed. - */ - public void undeclare() throws ZException { - int error = zenohc.zn_undeclare_publisher(pub); - if (error != 0) { - throw new ZException("zn_undeclare_publisher failed ", error); - } - } - - /** - * Send data in a compact_data message for the resource published by this - * Publisher. - * - * @param data the data to be sent. - * @throws ZException if write fails. - */ - public void streamCompactData(ByteBuffer data) throws ZException { - int result = zenohc.zn_stream_compact_data(pub, data); - if (result != 0) { - throw new ZException("zn_stream_compact_data of " + data.capacity() + " bytes buffer failed", result); - } - } - - /** - * Send data in a stream_data message for the resource published by this - * Publisher. - * - * @param data the data to be sent. - * @throws ZException if write fails. - */ - public void streamData(ByteBuffer data) throws ZException { - int result = zenohc.zn_stream_data(pub, data); - if (result != 0) { - throw new ZException("zn_stream_data of " + data.capacity() + " bytes buffer failed", result); - } - } - - /** - * Send data in a stream_data message for the resource published by this - * Publisher. - * - * @param data the data to be sent. - * @param encoding a metadata information associated with the published data - * that represents the encoding of the published data. - * @param kind a metadata information associated with the published data - * that represents the kind of publication. - * @throws ZException if write fails. - */ - public void streamData(ByteBuffer data, short encoding, short kind) throws ZException { - int result = zenohc.zn_stream_data_wo(pub, data, encoding, kind); - if (result != 0) { - throw new ZException("zn_stream_data of " + data.capacity() + " bytes buffer failed", result); - } - } - -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/QueryDest.java b/zenoh/src/main/java/org/eclipse/zenoh/net/QueryDest.java deleted file mode 100644 index 4b257605..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/QueryDest.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import org.eclipse.zenoh.core.ZException; -import org.eclipse.zenoh.swig.zn_query_dest_t; - -/** - * A data structure defining which storages or evals should be destination of a - * query (see - * {@link Session#query(String, String, ReplyCallback, QueryDest, QueryDest)}). - */ -public class QueryDest extends zn_query_dest_t { - - /** - * The Query destination kind. - */ - public enum Kind { - /** - * The nearest complete storage/eval if there is one, all storages/evals if not. - */ - ZN_BEST_MATCH((short) 0), - - /** - * Only complete storages/evals. - */ - ZN_COMPLETE((short) 1), - - /** - * All storages/evals. - */ - ZN_ALL((short) 2), - - /** - * no storages/evals. - */ - ZN_NONE((short) 3); - - private short numVal; - - Kind(short numVal) { - this.numVal = numVal; - } - - public static Kind fromInt(short numVal) throws ZException { - if (numVal == ZN_BEST_MATCH.value()) { - return ZN_BEST_MATCH; - } else if (numVal == ZN_COMPLETE.value()) { - return ZN_COMPLETE; - } else if (numVal == ZN_ALL.value()) { - return ZN_ALL; - } else if (numVal == ZN_NONE.value()) { - return ZN_NONE; - } else { - throw new ZException("INTERNAL ERROR: cannot create QueryDest.Kind from int: " + numVal); - } - } - - public short value() { - return numVal; - } - } - - private static QueryDest BEST_MATCH = new QueryDest(Kind.ZN_BEST_MATCH); - private static QueryDest COMPLETE = new QueryDest(Kind.ZN_COMPLETE); - private static QueryDest ALL = new QueryDest(Kind.ZN_ALL); - private static QueryDest NONE = new QueryDest(Kind.ZN_NONE); - - private QueryDest(Kind kind) { - super(); - setKind(kind.value()); - setNb((short) 1); - } - - private QueryDest(Kind kind, short nb) { - super(); - setKind(kind.value()); - setNb(nb); - } - - /** - * @return a {@link QueryDest} with kind {@link Kind#ZN_BEST_MATCH}. - */ - public static QueryDest bestMatch() { - return BEST_MATCH; - } - - /** - * @return a {@link QueryDest} with kind {@link Kind#ZN_COMPLETE}. - */ - public static QueryDest complete() { - return COMPLETE; - } - - /** - * Returns a {@link QueryDest} with kind {@link Kind#ZN_COMPLETE} and with the - * number of storages or evals that should be destination of the query. - * - * @param nb the number of storages or evals that should be destination of the - * query - * @return a {@link QueryDest} with kind {@link Kind#ZN_COMPLETE}. - */ - public static QueryDest complete(short nb) { - return new QueryDest(Kind.ZN_COMPLETE, nb); - } - - /** - * @return a {@link QueryDest} with kind {@link Kind#ZN_ALL}. - */ - public static QueryDest all() { - return ALL; - } - - /** - * @return a {@link QueryDest} with kind {@link Kind#ZN_NONE}. - */ - public static QueryDest none() { - return NONE; - } -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/QueryHandler.java b/zenoh/src/main/java/org/eclipse/zenoh/net/QueryHandler.java deleted file mode 100644 index 7cb47d09..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/QueryHandler.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -/** - * A callback interface to be implemented for handling of queries on storages or - * evals. See {@link Session#declareStorage(String, StorageHandler)} and - * {@link Session#declareEval(String, QueryHandler)}. - */ -public interface QueryHandler { - - /** - * The method that will be called on reception of query matching the - * stored/evaluated resource selection. The implementation must provide the data - * matching the resource rname by calling the - * {@link RepliesSender#sendReplies(Resource[])} method with the data as - * argument. The {@link RepliesSender#sendReplies(Resource[])} method MUST be - * called but accepts empty data array. This call can be made in the current - * Thread or in a different Thread. - * - * @param rname the resource name of the queried data. - * @param predicate a string provided by the querier refining the data to be - * provided. - * @param repliesSender a {@link RepliesSender} on which the - * sendReplies() function MUST be called with the - * provided data as argument. - */ - public void handleQuery(String rname, String predicate, RepliesSender repliesSender); - -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/RepliesSender.java b/zenoh/src/main/java/org/eclipse/zenoh/net/RepliesSender.java deleted file mode 100644 index 624fa5ff..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/RepliesSender.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -/** - * Class to be used in a {@link QueryHandler} implementation to send back - * replies to a query. - */ -public final class RepliesSender { - - private long send_replies_ptr; - private long query_handle_ptr; - - private RepliesSender(long send_replies_ptr, long query_handle_ptr) { - this.send_replies_ptr = send_replies_ptr; - this.query_handle_ptr = query_handle_ptr; - } - - /** - * Send back the replies to the query associated with this {@link RepliesSender} - * object. - * - * @param replies the replies. - */ - public void sendReplies(Resource[] replies) { - org.eclipse.zenoh.swig.zenohc.call_replies_sender(send_replies_ptr, query_handle_ptr, replies); - } -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/ReplyHandler.java b/zenoh/src/main/java/org/eclipse/zenoh/net/ReplyHandler.java deleted file mode 100644 index adffc940..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/ReplyHandler.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -/** - * A callback interface to be implemented for the reception of replies for a - * query. See {@link Session#query(String, String, ReplyHandler)} and - * {@link Session#query(String, String, ReplyHandler, QueryDest, QueryDest)}. - */ -public interface ReplyHandler { - - /** - * The method that will be called on reception of replies to the query sent by - * {@link Session#query(String, String, ReplyHandler)} or - * {@link Session#query(String, String, ReplyHandler, QueryDest, QueryDest)}. - * - * @param reply is the actual reply. - */ - public void handleReply(ReplyValue reply); - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/ReplyValue.java b/zenoh/src/main/java/org/eclipse/zenoh/net/ReplyValue.java deleted file mode 100644 index 66811f3b..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/ReplyValue.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import org.eclipse.zenoh.core.ZException; -import java.nio.ByteBuffer; - -/** - * A data structure containing one of the replies to a query (see - * {@link ReplyHandler#handleReply(ReplyValue)}). - */ -public class ReplyValue { - - /** - * The reply message kind. - */ - public enum Kind { - - /** - * The reply contains some data from a storage. - */ - ZN_STORAGE_DATA(0), - - /** - * The reply indicates that no more data is expected from the specified storage. - */ - ZN_STORAGE_FINAL(1), - - /** - * The reply contains some data from an eval. - */ - ZN_EVAL_DATA(2), - - /** - * The reply indicates that no more data is expected from the specified eval. - */ - ZN_EVAL_FINAL(3), - - /** - * The reply indicates that no more replies are expected for the query. - */ - ZN_REPLY_FINAL(4); - - private int numVal; - - Kind(int numVal) { - this.numVal = numVal; - } - - public static Kind fromInt(int numVal) throws ZException { - if (numVal == ZN_STORAGE_DATA.value()) { - return ZN_STORAGE_DATA; - } else if (numVal == ZN_STORAGE_FINAL.value()) { - return ZN_STORAGE_FINAL; - } - if (numVal == ZN_EVAL_DATA.value()) { - return ZN_EVAL_DATA; - } else if (numVal == ZN_EVAL_FINAL.value()) { - return ZN_EVAL_FINAL; - } else if (numVal == ZN_REPLY_FINAL.value()) { - return ZN_REPLY_FINAL; - } else { - throw new ZException("INTERNAL ERROR: cannot create ReplyValue.Kind from int: " + numVal); - } - } - - public int value() { - return numVal; - } - } - - private Kind kind; - private byte[] srcid; - private long rsn; - private String rname; - private ByteBuffer data; - private DataInfo info; - - protected ReplyValue(int kind, byte[] srcid, long rsn, String rname, ByteBuffer data, DataInfo info) - throws ZException { - this(Kind.fromInt(kind), srcid, rsn, rname, data, info); - } - - protected ReplyValue(Kind kind, byte[] srcid, long rsn, String rname, ByteBuffer data, DataInfo info) { - this.kind = kind; - this.srcid = srcid; - this.rsn = rsn; - this.rname = rname; - this.data = data; - this.info = info; - } - - /** - * @return the Reply message kind. - */ - public Kind getKind() { - return kind; - } - - /** - * @return the unique identifier of the storage or eval that sent this reply - * when {@link ReplyValue#kind} equals {@link Kind#ZN_STORAGE_DATA}, - * {@link Kind#ZN_STORAGE_FINAL}, {@link Kind#ZN_EVAL_DATA} or - * {@link Kind#ZN_EVAL_FINAL}. - */ - public byte[] getSrcId() { - return srcid; - } - - /** - * @return the sequence number of the reply from the identified storage or eval - * when {@link ReplyValue#kind} equals {@link Kind#ZN_STORAGE_DATA}, - * {@link Kind#ZN_STORAGE_FINAL}, {@link Kind#ZN_EVAL_DATA} or - * {@link Kind#ZN_EVAL_FINAL}. - */ - public long getRsn() { - return rsn; - } - - /** - * @return the resource name of the received data when {@link ReplyValue#kind} - * equals {@link Kind#ZN_STORAGE_DATA} or {@link Kind#ZN_EVAL_DATA}. - */ - public String getRname() { - return rname; - } - - /** - * @return the received data when {@link ReplyValue#kind} equals - * {@link Kind#ZN_STORAGE_DATA} or {@link Kind#ZN_EVAL_DATA}. - */ - public ByteBuffer getData() { - return data; - } - - /** - * @return some meta information about the received data when - * {@link ReplyValue#kind} equals {@link Kind#ZN_STORAGE_DATA} or - * {@link Kind#ZN_EVAL_DATA}. - */ - public DataInfo getInfo() { - return info; - } -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/Resource.java b/zenoh/src/main/java/org/eclipse/zenoh/net/Resource.java deleted file mode 100644 index 2e39f778..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/Resource.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import java.nio.ByteBuffer; - -/** - * A resource with a name and a value (data). - */ -public class Resource { - - private String rname; - private ByteBuffer data; - private int encoding; - private int kind; - - public Resource(String rname, ByteBuffer data, int encoding, int kind) { - this.rname = rname; - this.data = data; - this.encoding = encoding; - this.kind = kind; - } - - /** - * @return the resource name. - */ - public String getRname() { - return rname; - } - - /** - * @return the resource value. - */ - public ByteBuffer getData() { - return data; - } - - /** - * @return the encoding of the resource value. - */ - public int getEncoding() { - return encoding; - } - - /** - * @return the kind of the resource. - */ - public int getKind() { - return kind; - } -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/Rname.java b/zenoh/src/main/java/org/eclipse/zenoh/net/Rname.java deleted file mode 100644 index 4967aaa9..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/Rname.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import org.eclipse.zenoh.swig.zenohc; - -/** - * Utility class for resource name. - */ -public class Rname { - - /** - * Return true if the resource name 'rname1' intersect with the resource name - * 'rname2'. - * - * @param rname1 a resource name - * @param rname2 a resrouce name - */ - public static boolean intersect(String rname1, String rname2) { - return zenohc.zn_rname_intersect(rname1, rname2) != 0; - } - -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/Session.java b/zenoh/src/main/java/org/eclipse/zenoh/net/Session.java deleted file mode 100644 index 71f4c216..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/Session.java +++ /dev/null @@ -1,310 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import org.scijava.nativelib.NativeLoader; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.eclipse.zenoh.core.ZException; -import org.eclipse.zenoh.swig.SWIGTYPE_p_zn_session_t; -import org.eclipse.zenoh.swig.result_kind; -import org.eclipse.zenoh.swig.zn_eval_p_result_t; -import org.eclipse.zenoh.swig.zn_pub_p_result_t; -import org.eclipse.zenoh.swig.zn_sto_p_result_t; -import org.eclipse.zenoh.swig.zn_sub_p_result_t; -import org.eclipse.zenoh.swig.zn_session_p_result_t; -import org.eclipse.zenoh.swig.zenohc; - -import java.util.Map; -import java.util.Map.Entry; - -/** - * A zenoh-net session. - */ -public class Session { - - private static final Logger LOG = LoggerFactory.getLogger("org.eclipse.zenoh.net"); - private static final Map.Entry[] EMPTY = new Map.Entry[0]; - - static { - try { - LOG.debug("Load native library: zenohc_java"); - NativeLoader.loadLibrary("zenohc_java"); - } catch (Exception e) { - LOG.error("Failed to load native library zenohc_java: {}", e.toString()); - if (LOG.isDebugEnabled()) { - e.printStackTrace(); - } - System.exit(-1); - } - } - - private SWIGTYPE_p_zn_session_t s; - - private Session(SWIGTYPE_p_zn_session_t s) { - this.s = s; - } - - /** - * Open a zenoh-net session. - * - * @param locator a string representing the network endpoint to which establish - * the session. A typical locator looks like this : - * "tcp/127.0.0.1:7447". If null, - * open() will scout and try to establish the session - * automatically. - * @return a Zenoh object representing the openned zenoh session.. - * @throws ZException if session etablishment fails. - */ - public static Session open(String locator) throws ZException { - return open(locator, null); - } - - /** - * Open a zenoh-net session. - * - * @param locator a string representing the network endpoint to which - * establish the session. A typical locator looks like this : - * "tcp/127.0.0.1:7447". If null, - * open() will scout and try to establish the session - * automatically. - * @param properties a map of properties that will be used to establish and - * configure the zenoh session. **properties** will typically - * contain the "username" and - * "password" informations needed to establish - * the zenoh session with a secured infrastructure. It can be - * set to null. - * @return a Zenoh object representing the openned zenoh session.. - * @throws ZException if session etablishment fails. - */ - public static Session open(String locator, Map properties) throws ZException { - LOG.debug("Call zn_open on {}", locator); - Entry[] entries = properties != null ? properties.entrySet().toArray(EMPTY) : null; - zn_session_p_result_t session_result = zenohc.zn_open(locator, entries); - if (session_result.getTag().equals(result_kind.Z_ERROR_TAG)) { - throw new ZException("zn_open failed", session_result.getValue().getError()); - } - SWIGTYPE_p_zn_session_t s = session_result.getValue().getSession(); - - new Thread(new Runnable() { - @Override - public void run() { - { - LOG.debug("Run zn_recv_loop"); - zenohc.zn_recv_loop(s); - } - } - }).start(); - - return new Session(s); - } - - /** - * Close the zenoh-net session. - * - * @throws ZException if close failed. - */ - public void close() throws ZException { - LOG.debug("Call zn_stop_recv_loop"); - int stop_result = zenohc.zn_stop_recv_loop(s); - if (stop_result != 0) { - throw new ZException("zn_stop_recv_loop failed", stop_result); - } - int close_result = zenohc.zn_close(s); - if (close_result != 0) { - throw new ZException("close_result failed", stop_result); - } - } - - /** - * @return a map of properties containing various informations about the - * established zenoh-net session. - */ - public Map info() { - return zenohc.zn_info(s); - } - - /** - * Declare a publication for resource name resource. - * - * @param resource the resource name to publish. - * @return the zenoh {@link Publisher}. - * @throws ZException if declaration fails. - */ - public Publisher declarePublisher(String resource) throws ZException { - LOG.debug("Call zn_declare_publisher for {}", resource); - zn_pub_p_result_t pub_result = zenohc.zn_declare_publisher(s, resource); - if (pub_result.getTag().equals(result_kind.Z_ERROR_TAG)) { - throw new ZException("zn_declare_publisher on " + resource + " failed ", pub_result.getValue().getError()); - } - return new Publisher(pub_result.getValue().getPub()); - } - - /** - * Declare a subscribtion for all published data matching the provided resource - * name resource. - * - * @param resource the resource name to subscribe to. - * @param mode the subscription mode. - * @param handler a {@link DataHandler} subclass implementing the callback - * function that will be called each time a data matching the - * subscribed resource name resource is received. - * @return the zenoh-net {@link Subscriber}. - * @throws ZException if declaration fails. - */ - public Subscriber declareSubscriber(String resource, SubMode mode, DataHandler handler) throws ZException { - LOG.debug("Call zn_declare_subscriber for {}", resource); - zn_sub_p_result_t sub_result = zenohc.zn_declare_subscriber(s, resource, mode, handler); - if (sub_result.getTag().equals(result_kind.Z_ERROR_TAG)) { - throw new ZException("zn_declare_subscriber on " + resource + " failed ", sub_result.getValue().getError()); - } - return new Subscriber(sub_result.getValue().getSub()); - } - - /** - * Declare a storage for all data matching the provided resource name - * resource. - * - * @param resource the resource selection to store. - * @param handler a {@link StorageHandler} subclass implementing the callback - * functions that will be called each time a data matching the - * stored resource name resource is received and each - * time a query for data matching the stored resource name - * resource is received. The - * {@link StorageHandler#handleQuery(String, String, RepliesSender)} - * function MUST call the provided - * {@link RepliesSender#sendReplies(Resource[])} function with - * the resulting data. - * {@link RepliesSender#sendReplies(Resource[])} can be called - * with an empty array. - * @return the zenoh {@link Storage}. - * @throws ZException if declaration fails. - */ - public Storage declareStorage(String resource, StorageHandler handler) throws ZException { - LOG.debug("Call zn_declare_storage for {}", resource); - zn_sto_p_result_t sto_result = zenohc.zn_declare_storage(s, resource, handler); - if (sto_result.getTag().equals(result_kind.Z_ERROR_TAG)) { - throw new ZException("zn_declare_storage on " + resource + " failed ", sto_result.getValue().getError()); - } - return new Storage(sto_result.getValue().getSto()); - } - - /** - * Declare an eval able to provide data matching the provided resource name - * resource. - * - * @param resource the resource to evaluate. - * @param handler a {@link QueryHandler} subclass implementing the the callback - * function that will be called each time a query for data - * matching the evaluated resource name resource is - * received. The - * {@link QueryHandler#handleQuery(String, String, RepliesSender)} - * function MUST call the provided - * {@link RepliesSender#sendReplies(Resource[])} function with - * the resulting data. - * {@link RepliesSender#sendReplies(Resource[])} can be called - * with an empty array. - * @return the Eval. - * @throws ZException if declaration fails. - */ - public Eval declareEval(String resource, QueryHandler handler) throws ZException { - LOG.debug("Call zn_declare_eval for {}", resource); - zn_eval_p_result_t eval_result = zenohc.zn_declare_eval(s, resource, handler); - if (eval_result.getTag().equals(result_kind.Z_ERROR_TAG)) { - throw new ZException("zn_declare_eval on " + resource + " failed ", eval_result.getValue().getError()); - } - return new Eval(eval_result.getValue().getEval()); - } - - /** - * Send data in a write_data message for the resource resource. - * - * @param resource the resource name of the data to be sent. - * @param payload the data. - * @throws ZException if write fails. - */ - public void writeData(String resource, java.nio.ByteBuffer payload) throws ZException { - int result = zenohc.zn_write_data(s, resource, payload); - if (result != 0) { - throw new ZException("zn_write_data of " + payload.capacity() + " bytes buffer on " + resource + "failed", - result); - } - } - - /** - * Send data in a write_data message for the resource resource. - * - * @param resource the resource name of the data to be sent. - * @param payload the data. - * @param encoding a metadata information associated with the published data - * that represents the encoding of the published data. - * @param kind a metadata information associated with the published data - * that represents the kind of publication. - * @throws ZException if write fails. - */ - public void writeData(String resource, java.nio.ByteBuffer payload, short encoding, short kind) throws ZException { - int result = zenohc.zn_write_data_wo(s, resource, payload, encoding, kind); - if (result != 0) { - throw new ZException( - "zn_write_data_wo of " + payload.capacity() + " bytes buffer on " + resource + "failed", result); - } - } - - /** - * Query data matching resource name resource. - * - * @param resource the resource to query. - * @param predicate a string that will be propagated to the storages and evals - * that should provide the queried data. It may allow them to - * filter, transform and/or compute the queried data. . - * @param handler a {@link ReplyHandler} subclass implementing the callback - * function that will be called on reception of the replies of - * the query. - * @throws ZException if fails. - */ - public void query(String resource, String predicate, ReplyHandler handler) throws ZException { - query(resource, predicate, handler, QueryDest.bestMatch(), QueryDest.bestMatch()); - } - - /** - * Query data matching resource name resource. - * - * @param resource the resource to query. - * @param predicate a string that will be propagated to the storages and - * evals that should provide the queried data. It may allow - * them to filter, transform and/or compute the queried - * data. . - * @param handler a {@link ReplyHandler} subclass implementing the - * callback function that will be called on reception of - * the replies of the query. - * @param dest_storages a {@link QueryDest} indicating which matching storages - * should be destination of the query. - * @param dest_evals a {@link QueryDest} indicating which matching evals - * should be destination of the query. - * @throws ZException if fails. - */ - public void query(String resource, String predicate, ReplyHandler handler, QueryDest dest_storages, - QueryDest dest_evals) throws ZException { - int result = zenohc.zn_query_wo(s, resource, predicate, handler, dest_storages, dest_evals); - if (result != 0) { - throw new ZException("zn_query on " + resource + " failed", result); - } - } - - protected static void LogException(Throwable e, String message) { - LOG.warn(message, e); - } - -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/Storage.java b/zenoh/src/main/java/org/eclipse/zenoh/net/Storage.java deleted file mode 100644 index 35793263..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/Storage.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import org.eclipse.zenoh.core.ZException; -import org.eclipse.zenoh.swig.zenohc; -import org.eclipse.zenoh.swig.zn_sto_t; - -/** - * A Storage (see {@link Session#declareStorage(String, StorageCallback)}). - */ -public class Storage { - - private zn_sto_t sto; - - protected Storage(zn_sto_t sto) { - this.sto = sto; - } - - /** - * Undeclare the Storage. - * - * @throws ZException if undeclaration failed. - */ - public void undeclare() throws ZException { - int error = zenohc.zn_undeclare_storage(sto); - if (error != 0) { - throw new ZException("zn_undeclare_storage failed ", error); - } - } - -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/StorageHandler.java b/zenoh/src/main/java/org/eclipse/zenoh/net/StorageHandler.java deleted file mode 100644 index f3c7d44c..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/StorageHandler.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -/** - * A callback interface to be implemented by a Storage. - */ -public interface StorageHandler extends DataHandler, QueryHandler { -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/SubMode.java b/zenoh/src/main/java/org/eclipse/zenoh/net/SubMode.java deleted file mode 100644 index d554f07a..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/SubMode.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import org.eclipse.zenoh.core.ZException; -import org.eclipse.zenoh.swig.zn_temporal_property_t; - -/** - * Subscription mode (used in - * {@link Session#declareSubscriber(String, SubMode, SubscriberCallback)}). - */ -public class SubMode extends org.eclipse.zenoh.swig.zn_sub_mode_t { - - /** - * The subscription mode kind. - */ - public enum Kind { - ZN_PUSH_MODE((short) 1), ZN_PULL_MODE((short) 2), ZN_PERIODIC_PUSH_MODE((short) 3), - ZN_PERIODIC_PULL_MODE((short) 4); - - private short numVal; - - Kind(short numVal) { - this.numVal = numVal; - } - - public static Kind fromInt(short numVal) throws ZException { - if (numVal == ZN_PUSH_MODE.value()) { - return ZN_PUSH_MODE; - } else if (numVal == ZN_PULL_MODE.value()) { - return ZN_PULL_MODE; - } else if (numVal == ZN_PERIODIC_PUSH_MODE.value()) { - return ZN_PERIODIC_PUSH_MODE; - } else if (numVal == ZN_PERIODIC_PULL_MODE.value()) { - return ZN_PERIODIC_PULL_MODE; - } else { - throw new ZException("INTERNAL ERROR: cannot create SubMode.Kind from int: " + numVal); - } - } - - public short value() { - return numVal; - } - } - - private static SubMode PUSH_MODE = new SubMode(Kind.ZN_PUSH_MODE); - private static SubMode PULL_MODE = new SubMode(Kind.ZN_PULL_MODE); - - private SubMode(Kind kind) { - super(); - setKind(kind.value()); - } - - private SubMode(Kind kind, int origin, int period, int duration) { - super(); - setKind(kind.value()); - zn_temporal_property_t tprop = new zn_temporal_property_t(); - tprop.setOrigin(origin); - tprop.setPeriod(period); - tprop.setDuration(duration); - setTprop(tprop); - } - - /** - * @return the push subscription mode. - */ - public static SubMode push() { - return PUSH_MODE; - } - - /** - * @return the pull subscription mode. - */ - public static SubMode pull() { - return PULL_MODE; - } - - /** - * @return a periodic push subscription mode with the specified temporal - * properties. - */ - public static SubMode periodicPush(int origin, int period, int duration) { - return new SubMode(Kind.ZN_PERIODIC_PUSH_MODE, origin, period, duration); - } - - /** - * @return a periodic pull subscription mode with the specified temporal - * properties. - */ - public static SubMode periodicPull(int origin, int period, int duration) { - return new SubMode(Kind.ZN_PERIODIC_PULL_MODE, origin, period, duration); - } -} diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/Subscriber.java b/zenoh/src/main/java/org/eclipse/zenoh/net/Subscriber.java deleted file mode 100644 index d9c089fb..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/Subscriber.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -import org.eclipse.zenoh.core.ZException; -import org.eclipse.zenoh.swig.zenohc; -import org.eclipse.zenoh.swig.zn_sub_t; - -/** - * A Subscriber (see - * {@link Session#declareSubscriber(String, SubMode, SubscriberCallback)}). - */ -public class Subscriber { - - private zn_sub_t sub; - - protected Subscriber(zn_sub_t sub) { - this.sub = sub; - } - - /** - * Pull data for the {@link SubMode.Kind#ZN_PULL_MODE} or - * {@link SubMode.Kind#ZN_PERIODIC_PULL_MODE} subscribtion. The pulled data will - * be provided by calling the - * {@link DataHandler#handleData(String, java.nio.ByteBuffer, DataInfo)} - * function provided to the - * {@link Session#declareSubscriber(String, SubMode, DataHandler)} function. - * - * @throws ZException if pull failed. - */ - public void pull() throws ZException { - int result = zenohc.zn_pull(sub); - if (result != 0) { - throw new ZException("zn_pull failed", result); - } - } - - /** - * Undeclare the Subscriber. - * - * @throws ZException if undeclaration failed. - */ - public void undeclare() throws ZException { - int error = zenohc.zn_undeclare_subscriber(sub); - if (error != 0) { - throw new ZException("zn_undeclare_subscriber failed ", error); - } - } - -} \ No newline at end of file diff --git a/zenoh/src/main/java/org/eclipse/zenoh/net/ZNet.java b/zenoh/src/main/java/org/eclipse/zenoh/net/ZNet.java deleted file mode 100644 index 008a1b99..00000000 --- a/zenoh/src/main/java/org/eclipse/zenoh/net/ZNet.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ -package org.eclipse.zenoh.net; - -public final class ZNet { - - public static final Integer INFO_PID_KEY = 0x00; - public static final Integer INFO_PEER_KEY = 0x01; - public static final Integer INFO_PEER_PID_KEY = 0x02; - - public static final Integer USER_KEY = 0x50; - public static final Integer PASSWD_KEY = 0x51; - -} \ No newline at end of file diff --git a/zenoh/src/main/swig/zenohc.i b/zenoh/src/main/swig/zenohc.i deleted file mode 100644 index 9573ffb1..00000000 --- a/zenoh/src/main/swig/zenohc.i +++ /dev/null @@ -1,783 +0,0 @@ -/* - * Copyright (c) 2017, 2020 ADLINK Technology Inc. - * - * 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: - * ADLINK zenoh team, - */ - -%module zenohc - - -/*----- typemap for basic types -------*/ -%include "stdint.i" - -/*----- typemap for z_vec_t to java.util.Map -------*/ -%typemap(jni) z_vec_t "jobject" -%typemap(jtype) z_vec_t "java.util.Map" -%typemap(jstype) z_vec_t "java.util.Map" -%typemap(out) z_vec_t %{ - $result = (*jenv)->NewObject(jenv, hash_map_class, hash_map_constr); - unsigned int len = z_vec_length(&$1); - for(unsigned int i = 0; i < len; ++i) { - zn_property_t *prop = (zn_property_t *)z_vec_get(&$1, i); - jobject jinteger = (*jenv)->NewObject(jenv, integer_class, integer_constr, prop->id); - jbyteArray jbytes = (*jenv)->NewByteArray(jenv, prop->value.length); - (*jenv)->SetByteArrayRegion(jenv, jbytes, 0, prop->value.length, (jbyte*)prop->value.elem); - (*jenv)->CallObjectMethod(jenv, $result, map_put_method, jinteger, jbytes); - } -%} -%typemap(javaout) z_vec_t { - return $jnicall; -} - -/*----- typemap for java.util.Map to z_vec_t* -------*/ -%typemap(jni) (const z_vec_t *ps) "jobject" -%typemap(jtype) (const z_vec_t *ps) "java.util.Map.Entry[]" -%typemap(jstype) (const z_vec_t *ps) "java.util.Map.Entry[]" -%typemap(javain) (const z_vec_t *ps) "$javainput" -%typemap(in) (const z_vec_t *ps) %{ - z_vec_t vec; - if($input != NULL) { - jsize len = (*jenv)->GetArrayLength(jenv, $input); - assert_no_exception; - vec = z_vec_make(len); - for (int i = 0; i < len; ++i) { - jobject entry = (jobject) (*jenv)->GetObjectArrayElement(jenv, $input, i); - - jobject keyobj = (jobject) (*jenv)->CallObjectMethod(jenv, entry, entry_getKey_method); - jobject valobj = (jobject) (*jenv)->CallObjectMethod(jenv, entry, entry_getValue_method); - - int key = (*jenv)->CallIntMethod(jenv, keyobj, integer_intValue_method); - - z_uint8_array_t val = { - (*jenv)->GetArrayLength(jenv, valobj), - (uint8_t *) (*jenv)->GetByteArrayElements(jenv, valobj, NULL) - }; - - z_vec_append(&vec, zn_property_make(key, val)); - } - $1 = &vec; - } -%} - - -/*----- typemap for payload+length IN argument to ByteBuffer -------*/ -%typemap(jni) (const unsigned char *payload, size_t len) "jobject" -%typemap(jtype) (const unsigned char *payload, size_t len) "java.nio.ByteBuffer" -%typemap(jstype) (const unsigned char *payload, size_t len) "java.nio.ByteBuffer" -%typemap(javain, pre=" assert $javainput.isDirect() : \"Buffer must be allocated direct.\";") (const unsigned char *payload, size_t len) "$javainput" -// %typemap(javaout) (const unsigned char *payload, size_t len) { -// return $jnicall; -// } -%typemap(in) (const unsigned char *payload, size_t len) %{ - jbuffer_to_native(jenv, $input, $1, $2); -%} -%typemap(freearg) (const unsigned char *payload, size_t len) %{ - release_intermediate_byte_array(jenv, $input, $1, $2); -%} -// %typemap(memberin) (const unsigned char *payload, size_t len) %{ -// if ($input) { -// $1 = $input; -// } else { -// $1 = 0; -// } -// %} - -/*----- typemap for zn_on_disconnect_t : erase it in Java and pass NULL to C -------*/ -%typemap(in, numinputs=0) zn_on_disconnect_t on_disconnect %{ - $1 = NULL; -%} - - -/*----- typemap for zn_data_handler_t + arg in zn_declare_subscriber -------*/ -%typemap(jni) zn_data_handler_t data_handler "jobject"; -%typemap(jtype) zn_data_handler_t data_handler "org.eclipse.zenoh.net.DataHandler"; -%typemap(jstype) zn_data_handler_t data_handler "org.eclipse.zenoh.net.DataHandler"; -%typemap(javain) zn_data_handler_t data_handler "$javainput"; -%typemap(in,numinputs=1) (zn_data_handler_t data_handler, void *arg) %{ - // Store DataHandler object in a handler_arg - // that will be passed to jni_handledata() at each notification - handler_arg *jarg = malloc(sizeof(handler_arg)); - jarg->handler_object = (*jenv)->NewGlobalRef(jenv, $input); - jarg->context = NULL; - (*jenv)->DeleteLocalRef(jenv, $input); - - $1 = jni_handledata; - $2 = jarg; -%}; - -/*----- typemap for zn_data_handler_t + zn_query_handler_t + arg in zn_declare_storage -------*/ -%typemap(jni) (zn_data_handler_t data_handler, zn_query_handler_t query_handler) "jobject"; -%typemap(jtype) (zn_data_handler_t data_handler, zn_query_handler_t query_handler) "org.eclipse.zenoh.net.StorageHandler"; -%typemap(jstype) (zn_data_handler_t data_handler, zn_query_handler_t query_handler) "org.eclipse.zenoh.net.StorageHandler"; -%typemap(javain) (zn_data_handler_t data_handler, zn_query_handler_t query_handler) "$javainput"; -%typemap(in,numinputs=1) (zn_data_handler_t data_handler, zn_query_handler_t query_handler, void *arg) %{ - // Store the StorageHandler object in a handler_arg - // that will be passed to each call to jni_handledata and jni_handlequery - handler_arg *jarg = malloc(sizeof(handler_arg)); - jarg->handler_object = (*jenv)->NewGlobalRef(jenv, $input); - jarg->context = NULL; - (*jenv)->DeleteLocalRef(jenv, $input); - - $1 = jni_handledata; - $2 = jni_handlequery; - $3 = jarg; -%}; - -/*----- typemap for zn_query_handler_t + arg in zn_declare_eval -------*/ -%typemap(jni) (zn_query_handler_t query_handler) "jobject"; -%typemap(jtype) (zn_query_handler_t query_handler) "org.eclipse.zenoh.net.QueryHandler"; -%typemap(jstype) (zn_query_handler_t query_handler) "org.eclipse.zenoh.net.QueryHandler"; -%typemap(javain) (zn_query_handler_t query_handler) "$javainput"; -%typemap(in,numinputs=1) (zn_query_handler_t query_handler, void *arg) %{ - // Store the QueryHandler object in a handler_arg - // that will be passed to each call to jni_handlequery - handler_arg *jarg = malloc(sizeof(handler_arg)); - jarg->handler_object = (*jenv)->NewGlobalRef(jenv, $input); - jarg->context = NULL; - (*jenv)->DeleteLocalRef(jenv, $input); - - $1 = jni_handlequery; - $2 = jarg; -%}; - -/*----- typemap for zn_reply_handler_t + arg in zn_query -------*/ -%typemap(jni) zn_reply_handler_t reply_handler "jobject"; -%typemap(jtype) zn_reply_handler_t reply_handler "org.eclipse.zenoh.net.ReplyHandler"; -%typemap(jstype) zn_reply_handler_t reply_handler "org.eclipse.zenoh.net.ReplyHandler"; -%typemap(javain) zn_reply_handler_t reply_handler "$javainput"; -%typemap(in,numinputs=1) (zn_reply_handler_t reply_handler, void *arg) %{ - // Store ReplyHandler object in a handler_arg - // that will be passed to jni_handlereply() at each notification - handler_arg *jarg = malloc(sizeof(handler_arg)); - jarg->handler_object = (*jenv)->NewGlobalRef(jenv, $input); - jarg->context = NULL; - (*jenv)->DeleteLocalRef(jenv, $input); - - $1 = jni_handlereply; - $2 = jarg; -%}; - -/*----- typemap for Resource[] to zn_resource_p_array_t -------*/ -%typemap(jni) (zn_resource_p_array_t replies) "jobjectArray" -%typemap(jtype) (zn_resource_p_array_t replies) "org.eclipse.zenoh.net.Resource[]" -%typemap(jstype) (zn_resource_p_array_t replies) "org.eclipse.zenoh.net.Resource[]" -%typemap(javain) (zn_resource_p_array_t replies) "$javainput" -%typemap(in) (zn_resource_p_array_t replies) %{ - // Convert org.eclipse.zenoh.net.Resource[] into zn_resource_p_array_t - if ($input == NULL) { - $1.length = 0; - $1.elem = NULL; - } else { - jsize len = (*jenv)->GetArrayLength(jenv, $input); - assert_no_exception; - $1.length = len; - $1.elem = (zn_resource_t**)malloc(sizeof(zn_resource_t *) * $1.length); - for (int i = 0; i < len; ++i) { - jobject jres = (*jenv)->GetObjectArrayElement(jenv, $input, i); - $1.elem[i] = (zn_resource_t *)malloc(sizeof(zn_resource_t)); - - // rname - jstring jrname = (jstring) (*jenv)->CallObjectMethod(jenv, jres, resource_get_rname_method); - $1.elem[i]->rname = (*jenv)->GetStringUTFChars(jenv, jrname, 0); - assert_no_exception; - $1.elem[i]->context = (void*) jrname; - - // data + length - jobject jbuffer = (*jenv)->CallObjectMethod(jenv, jres, resource_get_data_method); - assert_no_exception; - jbuffer_to_native(jenv, jbuffer, $1.elem[i]->data, $1.elem[i]->length); - - // encoding and kind - $1.elem[i]->encoding = (*jenv)->CallIntMethod(jenv, jres, resource_get_encoding_method); - assert_no_exception; - $1.elem[i]->kind = (*jenv)->CallIntMethod(jenv, jres, resource_get_kind_method); - assert_no_exception; - } - } -%} -%typemap(freearg) (const unsigned char *payload, size_t len) %{ - release_intermediate_byte_array(jenv, $input, $1, $2); -%} - - -%{ -#include -#include "zenoh/net/private/msg.h" -#include "zenoh/net/config.h" -#include "zenoh/net/recv_loop.h" -#include "zenoh/types.h" -#include "zenoh/codec.h" -#include "zenoh/rname.h" -#include "zenoh.h" -#include - -#if (ZENOH_DEBUG == 0) -#define assert_no_exception -#else -#define assert_no_exception \ - if ((*jenv)->ExceptionCheck(jenv)) { \ - jthrowable jex = (*jenv)->ExceptionOccurred(jenv); \ - (*jenv)->Throw(jenv, jex); \ - } -#endif - - -/*------ Caching of Java VM, classes, methods... ------*/ -JavaVM *jvm = NULL; -jclass session_class = NULL; -jclass integer_class = NULL; -jclass hash_map_class = NULL; -jclass byte_buffer_class = NULL; -jclass timestamp_class = NULL; -jclass data_info_class = NULL; -jclass reply_value_class = NULL; -jclass replies_sender_class = NULL; -jmethodID log_exception_method = NULL; -jmethodID integer_constr = NULL; -jmethodID integer_intValue_method = NULL; -jmethodID hash_map_constr = NULL; -jmethodID map_put_method = NULL; -jmethodID entry_getKey_method = NULL; -jmethodID entry_getValue_method = NULL; -jmethodID byte_buffer_is_direct_method = NULL; -jmethodID byte_buffer_has_array_method = NULL; -jmethodID byte_buffer_array_method = NULL; -jmethodID byte_buffer_array_offset_method = NULL; -jmethodID byte_buffer_position_method = NULL; -jmethodID byte_buffer_remaining_method = NULL; -jmethodID byte_buffer_wrap_method = NULL; -jmethodID handledata_method = NULL; -jmethodID handlequery_method = NULL; -jmethodID replies_sender_constr = NULL; -jmethodID handlereply_method = NULL; -jmethodID timestamp_constr = NULL; -jmethodID data_info_constr = NULL; -jmethodID reply_value_constr = NULL; -jmethodID resource_get_rname_method = NULL; -jmethodID resource_get_data_method = NULL; -jmethodID resource_get_encoding_method = NULL; -jmethodID resource_get_kind_method = NULL; - - -jint JNI_OnLoad(JavaVM* vm, void* reserved) { - (void)reserved; // for warning supression as unused - jvm = vm; - JNIEnv* jenv; - if ((*vm)->GetEnv(vm, (void **) &jenv, JNI_VERSION_1_6) != JNI_OK) { - printf("Unexpected error retrieving JNIEnv in JNI_OnLoad()\n"); - return JNI_ERR; - } - - // Caching classes. Note that we need to convert those as a GlobalRef since they are local by default and might be GCed. - jclass s_class = (*jenv)->FindClass(jenv, "org/eclipse/zenoh/net/Session"); - assert_no_exception; - session_class = (jclass) (*jenv)->NewGlobalRef(jenv, s_class); - assert_no_exception; - jclass int_class = (*jenv)->FindClass(jenv, "java/lang/Integer"); - assert_no_exception; - integer_class = (jclass) (*jenv)->NewGlobalRef(jenv, int_class); - assert_no_exception; - jclass hm_class = (*jenv)->FindClass(jenv, "java/util/HashMap"); - assert_no_exception; - hash_map_class = (jclass) (*jenv)->NewGlobalRef(jenv, hm_class); - assert_no_exception; - jclass bb_class = (*jenv)->FindClass(jenv, "java/nio/ByteBuffer"); - assert_no_exception; - byte_buffer_class = (jclass) (*jenv)->NewGlobalRef(jenv, bb_class); - assert_no_exception; - jclass ts_class = (*jenv)->FindClass(jenv, "org/eclipse/zenoh/core/Timestamp"); - assert_no_exception; - timestamp_class = (jclass) (*jenv)->NewGlobalRef(jenv, ts_class); - assert_no_exception; - jclass di_class = (*jenv)->FindClass(jenv, "org/eclipse/zenoh/net/DataInfo"); - assert_no_exception; - data_info_class = (jclass) (*jenv)->NewGlobalRef(jenv, di_class); - assert_no_exception; - jclass rv_class = (*jenv)->FindClass(jenv, "org/eclipse/zenoh/net/ReplyValue"); - assert_no_exception; - reply_value_class = (jclass) (*jenv)->NewGlobalRef(jenv, rv_class); - assert_no_exception; - jclass rs_class = (*jenv)->FindClass(jenv, "org/eclipse/zenoh/net/RepliesSender"); - assert_no_exception; - replies_sender_class = (jclass) (*jenv)->NewGlobalRef(jenv, rs_class); - assert_no_exception; - - // Non-cached classes that are used below to get methods IDs - jclass map_class = (*jenv)->FindClass(jenv, "java/util/Map"); - assert_no_exception; - jclass map_entry_class = (*jenv)->FindClass(jenv, "java/util/Map$Entry"); - assert_no_exception; - jclass datahandler_class = (*jenv)->FindClass(jenv, "org/eclipse/zenoh/net/DataHandler"); - assert_no_exception; - jclass queryhandler_class = (*jenv)->FindClass(jenv, "org/eclipse/zenoh/net/QueryHandler"); - assert_no_exception; - jclass replyhandler_class = (*jenv)->FindClass(jenv, "org/eclipse/zenoh/net/ReplyHandler"); - assert_no_exception; - jclass resource_class = (*jenv)->FindClass(jenv, "org/eclipse/zenoh/net/Resource"); - assert_no_exception; - - - // Caching methods IDs. - log_exception_method = (*jenv)->GetStaticMethodID(jenv, session_class, - "LogException", "(Ljava/lang/Throwable;Ljava/lang/String;)V"); - assert_no_exception; - integer_constr = (*jenv)->GetMethodID(jenv, integer_class, - "", "(I)V"); - assert_no_exception; - integer_intValue_method = (*jenv)->GetMethodID(jenv, integer_class, - "intValue", "()I"); - assert_no_exception; - hash_map_constr = (*jenv)->GetMethodID(jenv, hash_map_class, - "", "()V"); - assert_no_exception; - map_put_method = (*jenv)->GetMethodID(jenv, map_class, - "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); - assert_no_exception; - entry_getKey_method = (*jenv)->GetMethodID(jenv, map_entry_class, - "getKey", "()Ljava/lang/Object;"); - assert_no_exception; - entry_getValue_method = (*jenv)->GetMethodID(jenv, map_entry_class, - "getValue", "()Ljava/lang/Object;"); - assert_no_exception; - byte_buffer_is_direct_method = (*jenv)->GetMethodID(jenv, byte_buffer_class, - "isDirect", "()Z"); - assert_no_exception; - byte_buffer_has_array_method = (*jenv)->GetMethodID(jenv, byte_buffer_class, - "hasArray", "()Z"); - assert_no_exception; - byte_buffer_array_method = (*jenv)->GetMethodID(jenv, byte_buffer_class, - "array", "()[B"); - assert_no_exception; - byte_buffer_array_offset_method = (*jenv)->GetMethodID(jenv, byte_buffer_class, - "arrayOffset", "()I"); - assert_no_exception; - byte_buffer_position_method = (*jenv)->GetMethodID(jenv, byte_buffer_class, - "position", "()I"); - assert_no_exception; - byte_buffer_remaining_method = (*jenv)->GetMethodID(jenv, byte_buffer_class, - "remaining", "()I"); - assert_no_exception; - byte_buffer_wrap_method = (*jenv)->GetStaticMethodID(jenv, byte_buffer_class, - "wrap", "([B)Ljava/nio/ByteBuffer;"); - assert_no_exception; - - handledata_method = (*jenv)->GetMethodID(jenv, datahandler_class, - "handleData", "(Ljava/lang/String;Ljava/nio/ByteBuffer;Lorg/eclipse/zenoh/net/DataInfo;)V"); - assert_no_exception; - handlequery_method = (*jenv)->GetMethodID(jenv, queryhandler_class, - "handleQuery", "(Ljava/lang/String;Ljava/lang/String;Lorg/eclipse/zenoh/net/RepliesSender;)V"); - assert_no_exception; - replies_sender_constr = (*jenv)->GetMethodID(jenv, replies_sender_class, - "", "(JJ)V"); - assert_no_exception; - handlereply_method = (*jenv)->GetMethodID(jenv, replyhandler_class, - "handleReply", "(Lorg/eclipse/zenoh/net/ReplyValue;)V"); - assert_no_exception; - timestamp_constr = (*jenv)->GetMethodID(jenv, timestamp_class, - "", "(J[B)V"); - data_info_constr = (*jenv)->GetMethodID(jenv, data_info_class, - "", "(JLorg/eclipse/zenoh/core/Timestamp;II)V"); - assert_no_exception; - reply_value_constr = (*jenv)->GetMethodID(jenv, reply_value_class, - "", "(I[BJLjava/lang/String;Ljava/nio/ByteBuffer;Lorg/eclipse/zenoh/net/DataInfo;)V"); - assert_no_exception; - resource_get_rname_method = (*jenv)->GetMethodID(jenv, resource_class, - "getRname", "()Ljava/lang/String;"); - resource_get_data_method = (*jenv)->GetMethodID(jenv, resource_class, - "getData", "()Ljava/nio/ByteBuffer;"); - resource_get_encoding_method = (*jenv)->GetMethodID(jenv, resource_class, - "getEncoding", "()I"); - resource_get_kind_method = (*jenv)->GetMethodID(jenv, resource_class, - "getKind", "()I"); - - return JNI_VERSION_1_6; -} - -void JNI_OnUnload(JavaVM *vm, void *reserved) { - (void)reserved; // for warning supression as unused - JNIEnv* env; - if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) { - printf("Unexpected error retrieving JNIEnv in JNI_OnUnload()\n"); - return; - } - - // Delete global references to cached classes - if (byte_buffer_class != NULL) { - (*env)->DeleteGlobalRef(env, byte_buffer_class); - byte_buffer_class = NULL; - } -} - -JNIEnv * get_jenv() { - JNIEnv * jenv; - int getEnvStat = (*jvm)->GetEnv(jvm, (void **)&jenv, JNI_VERSION_1_8); - if (getEnvStat == JNI_OK) { - // nothing to do - } else if (getEnvStat == JNI_EDETACHED) { - printf("JNI ERROR: the current thread is not attached to the JVM. Either attach it or use a Java thread.\n"); - assert(0); - } else if (getEnvStat == JNI_EVERSION) { - printf("JNI ERROR: JNI_VERSION_1_8 not supported\n"); - assert(0); - } else { - printf("JNI ERROR: unexpected status attaching current thread: %d\n", getEnvStat); - assert(0); - } - - return jenv; -} - -int catch_and_log_exception(JNIEnv* jenv, const char* msg) { - if ((*jenv)->ExceptionCheck(jenv)) { - jthrowable jex = (*jenv)->ExceptionOccurred(jenv); - jstring jmsg = (*jenv)->NewStringUTF(jenv, msg); - (*jenv)->CallStaticVoidMethod(jenv, session_class, log_exception_method, jex, jmsg); - (*jenv)->ExceptionClear(jenv); - (*jenv)->DeleteLocalRef(jenv, jmsg); - assert_no_exception; - return 1; - } - return 0; -} - - -// Convert a Java ByteBuffer declared as 'jbuffer' into an 'unsigned char *data' and a 'int length' -// NOTE: call release_intermediate_byte_array(jenv) after usage of jbuffer -#define jbuffer_to_native(jenv, jbuffer, data, length) \ - if ((*jenv)->CallIntMethod(jenv, jbuffer, byte_buffer_is_direct_method)) { \ - data = (unsigned char *) (*jenv)->GetDirectBufferAddress(jenv, jbuffer); \ - } else if ((*jenv)->CallIntMethod(jenv, jbuffer, byte_buffer_has_array_method)) { \ - jarray array = (jbyteArray) (*jenv)->CallObjectMethod(jenv, jbuffer, byte_buffer_array_method); \ - int offset = (int) (*jenv)->CallIntMethod(jenv, jbuffer, byte_buffer_array_offset_method); \ - int position = (int) (*jenv)->CallIntMethod(jenv, jbuffer, byte_buffer_position_method); \ - jboolean is_copy; \ - data = (unsigned char *) (*jenv)->GetByteArrayElements(jenv, array, &is_copy); \ - data = &data[offset+position]; \ - } else { \ - SWIG_JavaThrowException(jenv, SWIG_JavaRuntimeException, "The ByteBuffer is neither a direct buffer, neither a wrap of an array - it's not supported"); \ - } \ - length = (int) (*jenv)->CallIntMethod(jenv, jbuffer, byte_buffer_remaining_method); - -// Release an intermediate byte[] that may have been created by release_intermediate_byte_array -#define release_intermediate_byte_array(jenv, jbuffer, data, length) \ - if ((*jenv)->CallIntMethod(jenv, jbuffer, byte_buffer_has_array_method)) { \ - jarray array = (jbyteArray) (*jenv)->CallObjectMethod(jenv, jbuffer, byte_buffer_array_method); \ - int offset = (int) (*jenv)->CallIntMethod(jenv, jbuffer, byte_buffer_array_offset_method); \ - int position = (int) (*jenv)->CallIntMethod(jenv, jbuffer, byte_buffer_position_method); \ - (*jenv)->ReleaseByteArrayElements(jenv, array, (jbyte*) &data[-offset-position], JNI_ABORT); \ - } - -// Convert an 'unsigned char *data' and a 'int length' into a Java ByteBuffer declared as 'jbuffer' -#define native_to_jbuffer(jenv, data, length, jbuffer) \ - jbyteArray jbuffer_array = (*jenv)->NewByteArray(jenv, length); \ - assert_no_exception; \ - (*jenv)->SetByteArrayRegion(jenv, jbuffer_array, 0, length, (const jbyte*) data); \ - assert_no_exception; \ - jbuffer = (*jenv)->CallStaticObjectMethod(jenv, byte_buffer_class, byte_buffer_wrap_method, jbuffer_array); \ - assert_no_exception; \ - (*jenv)->DeleteLocalRef(jenv, jbuffer_array); \ - assert_no_exception; - -// delete a Java ByteBuffer created by native_to_jbuffer -#define delete_jbuffer(jenv, jbuffer) \ - (*jenv)->DeleteLocalRef(jenv, jbuffer); \ - assert_no_exception; - - - -typedef struct { - jobject handler_object; - void *context; -} handler_arg; - - -void jni_handledata(const zn_resource_key_t *rkey, const unsigned char *data, size_t length, const zn_data_info_t *info, void *arg) { - handler_arg *jarg = arg; - JNIEnv *jenv = get_jenv(); - - jstring jrname = NULL; - if (rkey->kind == ZN_STR_RES_KEY) { - jrname = (*jenv)->NewStringUTF(jenv, rkey->key.rname); - } else { - printf("INTERNAL ERROR: jni_handledata received a non-string zn_resource_key_t with kind=%d", rkey->kind); - return; - } - - jobject jbuffer; - native_to_jbuffer(jenv, data, length, jbuffer); - - jbyteArray jclockid = (*jenv)->NewByteArray(jenv, 16); - assert_no_exception; - (*jenv)->SetByteArrayRegion(jenv, jclockid, 0, 16, (const jbyte*) info->tstamp.clock_id); - assert_no_exception; - jobject jtstamp = (*jenv)->NewObject(jenv, timestamp_class, timestamp_constr, info->tstamp.time, jclockid); - assert_no_exception; - - jobject jinfo = (*jenv)->NewObject(jenv, data_info_class, data_info_constr, info->flags, jtstamp, info->encoding, info->kind); - assert_no_exception; - - // Call DataHandler.handleData() - (*jenv)->CallVoidMethod(jenv, jarg->handler_object, handledata_method, jrname, jbuffer, jinfo); - catch_and_log_exception(jenv, "Exception caught calling DataHandler.handleData()"); - - (*jenv)->DeleteLocalRef(jenv, jinfo); - assert_no_exception; - (*jenv)->DeleteLocalRef(jenv, jtstamp); - assert_no_exception; - (*jenv)->DeleteLocalRef(jenv, jclockid); - assert_no_exception; - delete_jbuffer(jenv, jbuffer); - (*jenv)->DeleteLocalRef(jenv, jrname); - assert_no_exception; -} - -void jni_handlequery(const char *rname, const char *predicate, zn_replies_sender_t send_replies, void *query_handle, void *arg) { - handler_arg *jarg = arg; - JNIEnv *jenv = get_jenv(); - - if (jarg->context != NULL) { - printf("Internal error in jni_handlequery: cannot serve query, as their is already an ongoing query (context is not NULL)\n"); - zn_resource_p_array_t replies; - replies.length = 0; - replies.elem = NULL; - send_replies(query_handle, replies); - return; - } - - jstring jrname = (*jenv)->NewStringUTF(jenv, rname); - jstring jpredicate = (*jenv)->NewStringUTF(jenv, predicate); - - // Create RepliesSender object - jlong send_replies_ptr = (jlong)send_replies; - jlong query_handle_ptr = (jlong)query_handle; - jobject jrepliesSender = (*jenv)->NewObject(jenv, replies_sender_class, replies_sender_constr, - send_replies_ptr, query_handle_ptr); - assert_no_exception; - - // Call QueryHandler.handleQuery() - (*jenv)->CallVoidMethod(jenv, jarg->handler_object, handlequery_method, jrname, jpredicate, jrepliesSender); - if (catch_and_log_exception(jenv, "XXXXX Exception caught calling QueryHandler.handleQuery()")) { - zn_resource_p_array_t replies = {0, 0}; - send_replies(query_handle, replies); - } - - (*jenv)->DeleteLocalRef(jenv, jrepliesSender); - assert_no_exception; - (*jenv)->DeleteLocalRef(jenv, jrname); - assert_no_exception; - (*jenv)->DeleteLocalRef(jenv, jpredicate); - assert_no_exception; -} - -void jni_handlereply(const zn_reply_value_t *reply, void *arg) { - handler_arg *jarg = arg; - JNIEnv *jenv = get_jenv(); - jbyteArray jsrcid = 0; - jstring jrname = 0; - jbyteArray jclockid = 0; - jobject jtstamp = 0; - jobject jinfo = 0; - jobject jbuffer = 0; - - if (reply->kind != ZN_REPLY_FINAL) { - jsrcid = (*jenv)->NewByteArray(jenv, reply->srcid_length); - assert_no_exception; - (*jenv)->SetByteArrayRegion(jenv, jsrcid, 0, reply->srcid_length, (const jbyte*) reply->srcid); - assert_no_exception; - - if (reply->kind == ZN_STORAGE_DATA || reply->kind == ZN_EVAL_DATA) { - jrname = (*jenv)->NewStringUTF(jenv, reply->rname); - - jclockid = (*jenv)->NewByteArray(jenv, 16); - assert_no_exception; - (*jenv)->SetByteArrayRegion(jenv, jclockid, 0, 16, (const jbyte*) reply->info.tstamp.clock_id); - assert_no_exception; - jtstamp = (*jenv)->NewObject(jenv, timestamp_class, timestamp_constr, - reply->info.tstamp.time, jclockid); - assert_no_exception; - - jinfo = (*jenv)->NewObject(jenv, data_info_class, data_info_constr, - reply->info.flags, jtstamp, reply->info.encoding, reply->info.kind); - assert_no_exception; - - native_to_jbuffer(jenv, reply->data, reply->data_length, jbuffer); - } - } - - jobject jreply = (*jenv)->NewObject(jenv, reply_value_class, reply_value_constr, - reply->kind, jsrcid, reply->rsn, jrname, jbuffer, jinfo); - - // Call ReplyHandler.handleReply() - (*jenv)->CallVoidMethod(jenv, jarg->handler_object, handlereply_method, jreply); - catch_and_log_exception(jenv, "Exception caught calling ReplyHandler.handleReply()"); - - (*jenv)->DeleteLocalRef(jenv, jreply); - assert_no_exception; - (*jenv)->DeleteLocalRef(jenv, jrname); - assert_no_exception; - (*jenv)->DeleteLocalRef(jenv, jinfo); - assert_no_exception; - (*jenv)->DeleteLocalRef(jenv, jtstamp); - assert_no_exception; - (*jenv)->DeleteLocalRef(jenv, jclockid); - assert_no_exception; - if (reply->kind == ZN_STORAGE_DATA || reply->kind == ZN_EVAL_DATA) { - delete_jbuffer(jenv, jbuffer); - } - (*jenv)->DeleteLocalRef(jenv, jsrcid); - assert_no_exception; -} - -void call_replies_sender(jlong send_replies_ptr, jlong query_handle_ptr, zn_resource_p_array_t replies) { - zn_replies_sender_t send_replies = (zn_replies_sender_t)send_replies_ptr; - void* query_handle = (void*)query_handle_ptr; - send_replies(query_handle, replies); -} - -%} - -void call_replies_sender(jlong send_replies_ptr, jlong query_handle_ptr, zn_resource_p_array_t replies); - -#include - -// -// Directly include headers definint constants -// (others are partially copied below) -// -%javaconst(1); -%include "zenoh/net/result.h" -%include "zenoh/result.h" - - -// -// Copied from zenoh/types.h -// -typedef size_t z_vle_t; - -// -// Copied from zenoh/net/property.h -// -typedef struct { - z_vle_t origin; - z_vle_t period; - z_vle_t duration; -} zn_temporal_property_t; - -// -// Copied from zenoh/net/types.h -// -typedef struct { - uint8_t kind; - zn_temporal_property_t tprop; -} zn_sub_mode_t; - -typedef void (*zn_reply_handler_t)(const zn_reply_value_t *reply, void *arg); - -typedef void (*zn_data_handler_t)(const zn_resource_key_t *rkey, const unsigned char *data, size_t length, const zn_data_info_t *info, void *arg); - -typedef void (*zn_replies_sender_t)(void* query_handle, zn_resource_p_array_t replies); -typedef void (*zn_query_handler_t)(const char *rname, const char *predicate, zn_replies_sender_t send_replies, void *query_handle, void *arg); - -typedef struct { - zn_session_t *z; - z_vle_t rid; - z_vle_t id; -} zn_sub_t; - -typedef struct { - zn_session_t *z; - z_vle_t rid; - z_vle_t id; -} zn_sto_t; - -typedef struct { - zn_session_t *z; - z_vle_t rid; - z_vle_t id; -} zn_pub_t; - -typedef struct { - zn_session_t *z; - z_vle_t rid; - z_vle_t id; -} zn_eva_t; - -typedef struct { enum result_kind tag; union { zn_session_t * session; int error; } value;} zn_session_p_result_t; -typedef struct { enum result_kind tag; union { zn_sub_t * sub; int error; } value;} zn_sub_p_result_t; -typedef struct { enum result_kind tag; union { zn_pub_t * pub; int error; } value;} zn_pub_p_result_t; -typedef struct { enum result_kind tag; union { zn_sto_t * sto; int error; } value;} zn_sto_p_result_t; -typedef struct { enum result_kind tag; union { zn_eva_t * eval; int error; } value;} zn_eval_p_result_t; - -typedef struct { - uint8_t kind; - uint8_t nb; -} zn_query_dest_t; - -// -// Copied from zenoh/recv_loop.h -// -void* zn_recv_loop(zn_session_t* z); - -int zn_running(zn_session_t* z); - -int zn_start_recv_loop(zn_session_t* z); - -int zn_stop_recv_loop(zn_session_t* z); - - -// -// Copied from zenoh/net/session.h -// -z_vec_t -zn_scout(char* iface, size_t tries, size_t period); - -zn_session_p_result_t -zn_open(char* locator, zn_on_disconnect_t on_disconnect, const z_vec_t *ps); - -z_vec_t -zn_info(zn_session_t *z); - -zn_sub_p_result_t -zn_declare_subscriber(zn_session_t *z, const char* resource, const zn_sub_mode_t *sm, zn_data_handler_t data_handler, void *arg); - -zn_pub_p_result_t -zn_declare_publisher(zn_session_t *z, const char *resource); - -zn_sto_p_result_t -zn_declare_storage(zn_session_t *z, const char* resource, zn_data_handler_t data_handler, zn_query_handler_t query_handler, void *arg); - -zn_eval_p_result_t -zn_declare_eval(zn_session_t *z, const char* resource, zn_query_handler_t query_handler, void *arg); - -int zn_stream_compact_data(zn_pub_t *pub, const unsigned char *payload, size_t len); -int zn_stream_data(zn_pub_t *pub, const unsigned char *payload, size_t len); -int zn_write_data(zn_session_t *z, const char* resource, const unsigned char *payload, size_t len); - -int zn_stream_data_wo(zn_pub_t *pub, const unsigned char *payload, size_t len, uint8_t encoding, uint8_t kind); -int zn_write_data_wo(zn_session_t *z, const char* resource, const unsigned char *payload, size_t len, uint8_t encoding, uint8_t kind); - -int zn_pull(zn_sub_t *sub); - -int zn_query(zn_session_t *z, const char* resource, const char* predicate, zn_reply_handler_t reply_handler, void *arg); -int zn_query_wo(zn_session_t *z, const char* resource, const char* predicate, zn_reply_handler_t reply_handler, void *arg, zn_query_dest_t dest_storages, zn_query_dest_t dest_evals); - -int zn_undeclare_subscriber(zn_sub_t *z); -int zn_undeclare_publisher(zn_pub_t *z); -int zn_undeclare_storage(zn_sto_t *z); -int zn_undeclare_eval(zn_eva_t *z); - -int zn_close(zn_session_t *z); - - -// -// Copied from zenoh/rname.h -// -int zn_rname_intersect(char *c1, char *c2);