diff --git a/.github/workflows/java.yml b/.github/workflows/java.yml deleted file mode 100644 index 56d93c86a..000000000 --- a/.github/workflows/java.yml +++ /dev/null @@ -1,196 +0,0 @@ -#name: java -#on: -# pull_request: -# types: [ opened, synchronize ] -#jobs: -# build-test-java-linux-64: -# runs-on: ubuntu-20.04 -# steps: -# - name: Check out repository code -# uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # pin@v3.5.2 -# - uses: prefix-dev/setup-pixi@v0.5.1 -# with: -# pixi-version: v0.13.0 -# - name: Cache -# uses: actions/cache@v3 -# with: -# key: ${{ runner.os }}-${{ hashFiles('pixi.lock', 'Cargo.lock') }}-build-test-java-linux-64 -# path: | -# ~/.cargo -# target -# .pixi -# - name: Test Java -# run: pixi run test-java -# - name: Copy native lib -# run: | -# mkdir -p native/linux-64 -# cp target/release/libvegafusion_jni.so native/linux-64 -# - name: Upload artifacts -# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # pin@v3.1.2 -# with: -# name: jni-native -# path: | -# native -# -# build-test-java-osx-64: -# runs-on: macos-11 -# steps: -# - name: Check out repository code -# uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # pin@v3.5.2 -# - uses: prefix-dev/setup-pixi@v0.5.1 -# with: -# pixi-version: v0.13.0 -# - name: Cache -# uses: actions/cache@v3 -# with: -# key: ${{ runner.os }}-${{ hashFiles('pixi.lock', 'Cargo.lock') }}-build-test-java-osx-64 -# path: | -# ~/.cargo -# target -# .pixi -# - name: Test Java -# run: pixi run test-java -# - name: Copy native lib -# run: | -# mkdir -p native/osx-64 -# cp target/release/libvegafusion_jni.dylib native/osx-64 -# - name: Upload artifacts -# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # pin@v3.1.2 -# with: -# name: jni-native -# path: | -# native -# -# build-java-osx-arm64: -# runs-on: macos-11 -# steps: -# - name: Check out repository code -# uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # pin@v3.5.2 -# - uses: prefix-dev/setup-pixi@v0.5.1 -# with: -# pixi-version: v0.13.0 -# - name: Cache -# uses: actions/cache@v3 -# with: -# key: ${{ runner.os }}-${{ hashFiles('pixi.lock', 'Cargo.lock') }}-build-java-osx-arm64 -# path: | -# ~/.cargo -# target -# .pixi -# - name: Build jni library -# run: | -# pixi run python automation/download_rust_target.py aarch64-apple-darwin -# pixi run build-jni --target aarch64-apple-darwin -# - name: Copy native lib -# run: | -# mkdir -p native/osx-arm64 -# cp target/aarch64-apple-darwin/release/libvegafusion_jni.dylib native/osx-arm64 -# - name: Upload artifacts -# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # pin@v3.1.2 -# with: -# name: jni-native -# path: | -# native - -# build-test-java-win-64: -# runs-on: windows-2022 -# steps: -# - name: Check out repository code -# uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # pin@v3.5.2 -## # Using pixi action here results in error on cleanup: -## # EBUSY: resource busy or locked, unlink 'D:\a\vegafusion\vegafusion\.pixi\env\Library\lib\jvm\lib\modules' -## - uses: prefix-dev/setup-pixi@v0.5.1 -## with: -## pixi-version: v0.13.0 -## # So use manual install for now -# - name: Install pixi -# run: | -# iwr -useb https://pixi.sh/install.ps1 | iex -# echo "${HOME}\.pixi\bin" -# echo "${HOME}\.pixi\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append -# - name: Cache -# uses: actions/cache@v3 -# with: -# key: ${{ runner.os }}-${{ hashFiles('pixi.lock', 'Cargo.lock') }}-build-test-java-win-64 -# path: | -# ~/.cargo -# target -# .pixi -# - name: Test Java -# run: pixi run test-java-win -# - name: Copy native lib -# run: | -# mkdir -p native/win-64 -# cp target/release/vegafusion_jni.dll native/win-64 -# - name: Upload artifacts -# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # pin@v3.1.2 -# with: -# name: jni-native -# path: | -# native -# -# build-jar: -# runs-on: ubuntu-20.04 -# needs: -# - build-test-java-linux-64 -# - build-test-java-osx-64 -# - build-java-osx-arm64 -## - build-test-java-win-64 -# steps: -# - name: Check out repository code -# uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # pin@v3.5.2 -# - uses: prefix-dev/setup-pixi@v0.5.1 -# with: -# pixi-version: v0.13.0 -# - name: Cache -# uses: actions/cache@v3 -# with: -# key: ${{ runner.os }}-${{ hashFiles('pixi.lock', 'Cargo.lock') }}-build-jar -# path: | -# .pixi -# - name: Download jni libs -# uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # pin@v3.0.2 -# with: -# name: jni-native -# path: jni-native -# - name: Build jar -# run: | -# export VEGAFUSION_JNI_LIBRARIES=/home/runner/work/vegafusion/vegafusion/jni-native -# pixi run build-jar -# - name: Upload jar -# uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # pin@v3.1.2 -# with: -# name: jar -# path: | -# java/lib/build/libs/vegafusion-*.jar -# -# try-jar: -# strategy: -# fail-fast: false -# matrix: -# os: -# - ubuntu-20.04 -# - macos-11 -## - windows-2022 -# runs-on: ${{ matrix.os }} -# needs: [ build-jar ] -# steps: -# - name: Install Java -# uses: actions/setup-java@5ffc13f4174014e2d4d4572b3d74c3fa61aeb2c2 # pin@v3 -# with: -# distribution: 'temurin' -# java-version: '17' -# - name: Download jar -# uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # pin@v3.0.2 -# with: -# name: jar -# path: . -# - name: Run jar (non-Windows) -# if: ${{ runner.os != 'Windows' }} -# run: | -# java -jar vegafusion-*.jar -# - name: Run jar (Windows) -# if: ${{ runner.os == 'Windows' }} -# run: | -# $jarFile = Get-ChildItem -Path .\ -Filter "vegafusion-*.jar" | Select-Object -First 1 -# java -jar $jarFile diff --git a/.gitignore b/.gitignore index b08bab0b7..e094e0614 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,6 @@ Cargo.bak.lock # intellij /.idea/ -/java/.idea/ # pixi /.pixi/ diff --git a/BUILD.md b/BUILD.md index c8309f1cb..a383608d8 100644 --- a/BUILD.md +++ b/BUILD.md @@ -74,14 +74,6 @@ Then launch JupyterLab pixi run jupyter-lab ``` -## Build and test Java -Build and test the VegaFusion Java package with - -``` -pixi run test-java -``` - - ## Build Python packages for distribution To build Python wheels for the current platform, the `build-py-embed`, `build-py-vegafusion`, and `build-py-jupyter` tasks may be used diff --git a/Cargo.lock b/Cargo.lock index 75e6b7b27..757b984a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4945,19 +4945,6 @@ dependencies = [ "vegafusion-common", ] -[[package]] -name = "vegafusion-jni" -version = "1.6.9" -dependencies = [ - "jni", - "serde_json", - "tokio", - "vegafusion-common", - "vegafusion-core", - "vegafusion-runtime", - "vegafusion-sql", -] - [[package]] name = "vegafusion-runtime" version = "1.6.9" diff --git a/Cargo.toml b/Cargo.toml index b698e8f59..35ae2eb0a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,6 @@ members = [ "vegafusion-python", "vegafusion-wasm", "vegafusion-server", - "vegafusion-jni", ] [workspace.dependencies] diff --git a/RELEASE.md b/RELEASE.md index bf0af6901..3bbb38acc 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -70,30 +70,6 @@ npm run build:prod npm publish ``` -### Publish Java library -First, download and unzip the `jni-native` CI artifact. This artifact contains the compiled JNI libraries for each supported operating system and architecture. - -Set the `VEGAFUSION_JNI_LIBRARIES` environment variable to the unzipped `jni-native` directory and publish the jar with `pixi run publish-java`: - -``` -VEGAFUSION_JNI_LIBRARIES=/path/to/jni-native pixi run publish-java -``` - -This publishes the jar files to OSSRH at https://s01.oss.sonatype.org/. In order to sync these files to the public maven central repository, follow the steps described in https://central.sonatype.org/publish/release/. - -#### Java publication config -Publishing the Java library to maven central requires configuring the `~/.gradle/gradle.properties` file with: -``` -signing.keyId=YourKeyId -signing.password=YourPublicKeyPassword -signing.secretKeyRingFile=PathToYourKeyRingFile - -ossrhUsername=your-jira-id -ossrhPassword=your-jira-password -``` - - - ### Publish Rust crates The Rust crates should be published in the following order diff --git a/java/.gitignore b/java/.gitignore deleted file mode 100644 index 9e2a1bf21..000000000 --- a/java/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Ignore Gradle project-specific cache directory -.gradle - -# Ignore Gradle build output directory -build -/native/ diff --git a/java/README.md b/java/README.md deleted file mode 100644 index ca0c8e8ce..000000000 --- a/java/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# VegaFusion-java -This directory contains the Java interface to VegaFusion. This is the pure-Java logic that relies on the native jni library generated by the `vegafusion-jni` Rust crate. - -# Notes / Limitations - - Vega JSON specifications are input and output as strings. - - `VegaFusionRuntime.preTransformSpec` doesn't support inline datasets yet. - - `preTransformValues` isn't supported yet - - `preTransformExtract` isn't supported yet - - Custom SQL connections aren't supported yet (The default DataFusion connection is used) - -# Running tests -In order to run tests: - -``` -pixi run test-java -``` - -# Releasing -See [RELEASE.md](../RELEASE.md) for instructions on publishing to Maven Central. diff --git a/java/gradle/wrapper/gradle-wrapper.jar b/java/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index c1962a79e..000000000 Binary files a/java/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/java/gradle/wrapper/gradle-wrapper.properties b/java/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 37aef8d3f..000000000 --- a/java/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,6 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip -networkTimeout=10000 -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/java/gradlew b/java/gradlew deleted file mode 100755 index aeb74cbb4..000000000 --- a/java/gradlew +++ /dev/null @@ -1,245 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», -# «${var#prefix}», «${var%suffix}», and «$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/java/gradlew.bat b/java/gradlew.bat deleted file mode 100644 index 6689b85be..000000000 --- a/java/gradlew.bat +++ /dev/null @@ -1,92 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/java/lib/build.gradle b/java/lib/build.gradle deleted file mode 100644 index 79798c1bd..000000000 --- a/java/lib/build.gradle +++ /dev/null @@ -1,138 +0,0 @@ -plugins { - // Apply the java-library plugin for API and implementation separation. - id 'java-library' - id 'maven-publish' - id 'signing' -} - -repositories { - // Use Maven Central for resolving dependencies. - mavenCentral() -} - -dependencies { - // Use JUnit Jupiter for testing. - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1' - testImplementation 'com.fasterxml.jackson.core:jackson-databind:2.15.0' -} - -// Apply a specific Java toolchain to ease working on different environments. -java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } -} - -tasks.named('test') { - // Use JUnit Platform for unit tests. - useJUnitPlatform() -} - -def versionId = new File('version.txt').text.trim() - -tasks.register('sourcesJar', Jar) { - dependsOn classes - archiveClassifier.set('sources') - from sourceSets.main.allSource -} - -tasks.register('javadocJar', Jar) { - dependsOn javadoc - archiveClassifier.set('javadoc') - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} - -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - groupId = 'io.vegafusion' - artifactId = 'vegafusion' - version = versionId - artifact sourcesJar - artifact javadocJar - pom { - name = 'VegaFusion' - description = 'Server-side scaling for Vega visualizations' - url = 'https://vegafusion.io/' - licenses { - license { - name = 'BSD-3-Clause' - url = 'https://spdx.org/licenses/BSD-3-Clause.html' - } - } - developers { - developer { - id = 'jonmmease' - name = 'Jon Mease' - email = 'jonmmease@gmail.com' - } - } - scm { - connection = 'scm:git:https://github.com/hex-inc/vegafusion.git' - developerConnection = 'scm:git:git@github.com:hex-inc/vegafusion.git' - url = 'https://github.com/hex-inc/vegafusion' - } - } - } - } - repositories { - maven { - name = 'ossrh' - url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") - credentials { - def ossrhUsernameProvider = providers.gradleProperty('ossrhUsername') - def ossrhPasswordProvider = providers.gradleProperty('ossrhPassword') - - username = ossrhUsernameProvider.orNull - password = ossrhPasswordProvider.orNull - } - } - } -} - -signing { - sign publishing.publications.mavenJava -} - -jar { - manifest { - attributes "Main-Class": "io.vegafusion.VegaFusionRuntime" - } - archiveBaseName.set('vegafusion') - archiveVersion.set(versionId) - - // Validate that VEGAFUSION_JNI_LIBRARIES is set and valid - doFirst { - def nativeLibDir = System.getenv('VEGAFUSION_JNI_LIBRARIES') - if (nativeLibDir == null) { - throw new GradleException( - 'VEGAFUSION_JNI_LIBRARIES environment variable is not set' - ) - } - - // Validate the expected subdirectories - def subdirs = [ - 'linux-64', - 'osx-64', - 'osx-arm64', -// 'win-64' - ] - subdirs.each { subdir -> - def subdirPath = new File(nativeLibDir, subdir) - if (!subdirPath.isDirectory()) { - throw new GradleException("Expected subdirectory $subdir does not exist in $nativeLibDir") - } - } - } - - def nativeLibDir = System.getenv('VEGAFUSION_JNI_LIBRARIES') - from(nativeLibDir) { - into "native" - } -} diff --git a/java/lib/src/main/java/io/vegafusion/VegaFusionException.java b/java/lib/src/main/java/io/vegafusion/VegaFusionException.java deleted file mode 100644 index 91c82fc3a..000000000 --- a/java/lib/src/main/java/io/vegafusion/VegaFusionException.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.vegafusion; - -/** - * This class represents exceptions specific to VegaFusion - */ -public class VegaFusionException extends RuntimeException { - /** - * Constructs a new VegaFusionException with the specified detail message. - * - * @param message the detail message. The detail message is saved for - * later retrieval by the {@link #getMessage()} method. - */ - public VegaFusionException(String message) { - super(message); - } -} diff --git a/java/lib/src/main/java/io/vegafusion/VegaFusionRuntime.java b/java/lib/src/main/java/io/vegafusion/VegaFusionRuntime.java deleted file mode 100644 index ecd43ff1e..000000000 --- a/java/lib/src/main/java/io/vegafusion/VegaFusionRuntime.java +++ /dev/null @@ -1,239 +0,0 @@ -package io.vegafusion; - -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.io.IOException; - -/** - * This class provides a VegaFusion runtime that may be used to perform - * Vega transform operations. - */ -public class VegaFusionRuntime { - /** - * Returns the version of the VegaFusion - * - * @return A string storing the version of the VegaFusion - */ - public static native String version(); - - /** - * Creates a native VegaFusionRuntime object with specified capacity - * and memory limit. This is a native method, implemented in native code. - * - * @param capacity The capacity of the runtime cache - * @param memoryLimit The memory limit for the runtime cache - * @param numThreads The number of worker threads - * @return A long representing a pointer to the native runtime. - */ - private static native long innerCreate(long capacity, long memoryLimit, int numThreads); - - /** - * Destroys a native VegaFusionRuntime object. - * This is a native method, implemented in native code. - * - * @param pointer The pointer to the state to be destroyed. - */ - private static native void innerDestroy(long pointer); - - /** - * Patches a previously pre-transformed Vega specification. - * This is a native method, implemented in native code. - * - * @param spec1 Previous input to preTransformSpec - * @param preTransformedSpec1 Previous result of preTransformSpec - * @param spec2 New Vega spec that is potentially similar to spec1 - * @return The pre-transformed version of spec2, or null if patching - * is not possible. - */ - private static native String innerPatchPreTransformedSpec( - String spec1, String preTransformedSpec1, String spec2 - ); - - /** - * Evaluate the transforms in a Vega specification, returning a new - * specification with transformed data included inline. - * This is a native method, implemented in native code. - * - * @param pointer The pointer to the native VegaFusionRuntime - * @param spec The Vega specification to be transformed - * @param localTz The local time zone (defaults to "UTC" if null) - * @param defaultInputTz The default input time zone (defaults to localTz if null) - * @param rowLimit The row limit for the inline transformed data. - * If zero then no limit is imposed. - * @param preserveInteractivity Whether to preserve interactivity in the resulting - * Vega specification - * @return String containing the transformed specification. - Any warnings are added to the spec under usermeta.vegafusion_warnings - */ - private static native String innerPreTransformSpec( - long pointer, String spec, String localTz, String defaultInputTz, int rowLimit, boolean preserveInteractivity - ); - - static { - String libPath = System.getenv("VEGAFUSION_JNI_LIBRARY"); - if (libPath != null) { - // Use explicit path to jni library - System.load(libPath); - } else { - // Use library bundled in jar - try { - System.loadLibrary("vegafusion_jni"); - } catch (LinkageError e) { - // Build path based on os and architecture - - String osName = System.getProperty("os.name").toLowerCase(); - String osArch = System.getProperty("os.arch").toLowerCase(); - - String libName; - String directory; - if (osName.contains("win")) { - libName = "vegafusion_jni.dll"; - if (osArch.equals("amd64") || osArch.equals("x86_64")) { - directory = "win-64"; - } else { - throw new UnsupportedOperationException("Unsupported architecture for Windows: " + osArch); - } - } else if (osName.contains("mac")) { - libName = "libvegafusion_jni.dylib"; - if (osArch.equals("amd64") || osArch.equals("x86_64")) { - directory = "osx-64"; - } else if (osArch.equals("aarch64") || osArch.equals("arm64")) { - directory = "osx-arm64"; - } else { - throw new UnsupportedOperationException("Unsupported architecture for macOS: " + osArch); - } - } else if (osName.contains("nix") || osName.contains("nux")) { - libName = "libvegafusion_jni.so"; - if (osArch.equals("amd64") || osArch.equals("x86_64")) { - directory = "linux-64"; - } else { - throw new UnsupportedOperationException("Unsupported architecture for Linux: " + osArch); - } - } else { - throw new UnsupportedOperationException("Unsupported operating system: " + osName); - } - - // Path in the jar file to the compiled library - String libPathInJar = "/native/" + directory + "/" + libName; - - // Extract the library to a temporary file - InputStream libStream = VegaFusionRuntime.class.getResourceAsStream(libPathInJar); - if (libStream == null) { - throw new RuntimeException("Failed to find " + libPathInJar + " in jar file"); - } - - // Create a temp file and get its path - try { - String tempFileName = Paths.get(libName).getFileName().toString(); - java.nio.file.Path temp = Files.createTempFile(tempFileName, ""); - - // Copy the library to the temp file - Files.copy(libStream, temp, StandardCopyOption.REPLACE_EXISTING); - - // Load the library - System.load(temp.toAbsolutePath().toString()); - - // Schedule the temp file to be deleted on exit - temp.toFile().deleteOnExit(); - } catch (IOException ioe) { - e.printStackTrace(); - } - } - } - } - - private long state_ptr; - - /** - * Constructs a new VegaFusionRuntime with the specified cache capacity and memory limit. - * - * @param capacity The cache capacity (in number of cache entries) - * @param memoryLimit The cache memory limit (in bytes) - * @param numThreads The number of worker threads - */ - public VegaFusionRuntime(long capacity, long memoryLimit, int numThreads) { - state_ptr = VegaFusionRuntime.innerCreate(capacity, memoryLimit, numThreads); - } - - /** - * Destroys the native VegaFusionRuntime. - * The class instance should not be used after destroy is called. - */ - public void destroy() { - if (state_ptr != 0) { - innerDestroy(state_ptr); - state_ptr = 0; - } - } - - /** - * Patches a previously pre-transformed Vega specification. - * - * @param spec1 Previous input to preTransformSpec - * @param preTransformedSpec1 Previous result of preTransformSpec - * @param spec2 New Vega spec that is potentially similar to spec1 - * @return The pre-transformed version of spec2, or null if patching - * is not possible. - * @throws IllegalStateException if the destroy method was called previously - */ - public String patchPreTransformedSpec(String spec1, String preTransformedSpec1, String spec2) { - validate(); - return innerPatchPreTransformedSpec(spec1, preTransformedSpec1, spec2); - } - - /** - * Evaluate the transforms in a Vega specification, returning a new - * specification with transformed data included inline. - * - * @param spec The Vega specification to be transformed - * @param localTz The local time zone (defaults to "UTC" if null) - * @param defaultInputTz The default input time zone (defaults to localTz if null) - * @param rowLimit The row limit for the inline transformed data. - * If zero then no limit is imposed. - * @param preserveInteractivity Whether to preserve interactivity in the resulting - * Vega specification - * @return String containing the transformed specification. - Any warnings are added to the spec under usermeta.vegafusion_warnings. - * @throws IllegalStateException if the destroy method was called previously - */ - public String preTransformSpec(String spec, String localTz, String defaultInputTz, int rowLimit, boolean preserveInteractivity) { - validate(); - return innerPreTransformSpec(state_ptr, spec, localTz, defaultInputTz, rowLimit, preserveInteractivity); - } - - /** - * Checks if the VegaFusionRuntime state is valid - * (if the destroy method was not called previously) - * - * @return A boolean indicating whether the runtime is valid. - */ - public boolean isValid() { - return state_ptr != 0; - } - - /** - * Validates the native VegaFusionRuntime - * - * @throws IllegalStateException If the native VegaFusionRuntime is invalid. - */ - public void validate() throws IllegalStateException { - if (state_ptr == 0) { - throw new IllegalStateException("VegaFusionRuntime may not be used after calling destroy()"); - } - } - - /** - * The main entry point for the program. - * This method retrieves the version of the VegaFusionRuntime and prints it out, - * along with a brief description of VegaFusion. - * - * @param args An array of command-line arguments for the application. - */ - public static void main(String[] args) { - String version = VegaFusionRuntime.version(); - System.out.println("VegaFusion: Server-side scaling for Vega visualizations"); - System.out.println("Version: " + version); - } -} diff --git a/java/lib/src/test/java/io/vegafusion/VegaFusionRuntimeTest.java b/java/lib/src/test/java/io/vegafusion/VegaFusionRuntimeTest.java deleted file mode 100644 index 0b41b38fc..000000000 --- a/java/lib/src/test/java/io/vegafusion/VegaFusionRuntimeTest.java +++ /dev/null @@ -1,347 +0,0 @@ -package io.vegafusion; - -import java.io.IOException; -import java.nio.file.*; -import java.util.ArrayList; -import java.util.Map; -import java.util.List; - -import com.fasterxml.jackson.core.JsonProcessingException; -import org.junit.jupiter.api.Test; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JsonNode; - -import static org.junit.jupiter.api.Assertions.*; - -public class VegaFusionRuntimeTest { - private VegaFusionRuntime makeRuntime() { - return new VegaFusionRuntime(32, 1000000000, 4); - } - - @Test - void testVersion() throws IOException { - String expectedVersion = new String(Files.readAllBytes(Paths.get("../version.txt"))).trim(); - var version = VegaFusionRuntime.version(); - assertEquals(version, expectedVersion); - } - - @Test - void testCreate() { - VegaFusionRuntime runtime = makeRuntime(); - assertTrue(runtime.isValid()); - - // Destroy should invalidate - runtime.destroy(); - assertFalse(runtime.isValid()); - - // Destroy should be idempotent - runtime.destroy(); - assertFalse(runtime.isValid()); - } - - @Test - void testPatchPretransformedSpec() throws JsonProcessingException { - // Create runtime - VegaFusionRuntime runtime = makeRuntime(); - - // Define simple specs that are compatible with patching - String spec1 = "{\"width\": 100, \"height\": 200}"; - String preTransformedSpec1 = "{\"width\": 100, \"height\": 150}"; - String spec2 = "{\"width\": 150, \"height\": 200}"; - - // Perform patch - String preTransformedSpec2 = runtime.patchPreTransformedSpec(spec1, preTransformedSpec1, spec2); - - // Validate results - ObjectMapper mapper = new ObjectMapper(); - Map specMap = mapper.readValue( - preTransformedSpec2, new TypeReference<>(){} - ); - assertEquals(specMap.get("width").asInt(), 150); - assertEquals(specMap.get("height").asInt(), 150); - - // Cleanup - runtime.destroy(); - - // Calling again after destroy should raise an exception - assertThrows(IllegalStateException.class, () -> - runtime.patchPreTransformedSpec(spec1, preTransformedSpec1, spec2) - ); - } - - @Test - void testUnsuccessfulPatchPretransformedSpec() { - // Create runtime - VegaFusionRuntime runtime = makeRuntime(); - - // Define specs that are not compatible with patching - String spec1 = "{\"data\": [{\"name\": \"foo\"}]}"; - String preTransformedSpec1 = "{\"data\": []}"; - String spec2 = "{\"data\": [{\"name\": \"bar\"}]}"; - String preTransformedSpec2 = runtime.patchPreTransformedSpec(spec1, preTransformedSpec1, spec2); - assertNull(preTransformedSpec2); - - // Cleanup - runtime.destroy(); - - // Calling again after destroy should raise an exception - assertThrows(IllegalStateException.class, () -> - runtime.patchPreTransformedSpec(spec1, preTransformedSpec1, spec2) - ); - } - - @Test - void testInvalidPatchPretransformedSpec1() { - // Create runtime - VegaFusionRuntime runtime = makeRuntime(); - - // Define spec strings that are not valid JSON - String spec1 = "{\"data\""; - String preTransformedSpec1 = "{\"data\""; - String spec2 = "{\"data\""; - - VegaFusionException e = assertThrows(VegaFusionException.class, () -> - runtime.patchPreTransformedSpec(spec1, preTransformedSpec1, spec2) - ); - - assertTrue(e.getMessage().toLowerCase().contains("serde")); - - // Cleanup - runtime.destroy(); - } - - @Test - void testInvalidPatchPretransformedSpec2() { - // Create runtime - VegaFusionRuntime runtime = makeRuntime(); - - // Define spec strings that are valid JSON but invalid Vega specs - String spec1 = "{\"data\": 23}"; - String preTransformedSpec1 = "{}"; - String spec2 = "{}"; - - VegaFusionException e = assertThrows(VegaFusionException.class, () -> - runtime.patchPreTransformedSpec(spec1, preTransformedSpec1, spec2) - ); - - assertTrue(e.getMessage().toLowerCase().contains("serde")); - - // Cleanup - runtime.destroy(); - } - - - @Test - void testPretransformSpec() throws JsonProcessingException { - // Build VegaFusionRuntime - VegaFusionRuntime runtime = makeRuntime(); - - // Construct histogram spec - String spec = histSpec(); - - // Pre-transform spec with rowLimit of 3 so that inline data is truncated - String preTransformedSpecResult = runtime.preTransformSpec( - spec, "UTC", null, 3, true - ); - - // Parse spec as JSON - ObjectMapper mapper = new ObjectMapper(); - Map preTransformedSpec = mapper.readValue( - preTransformedSpecResult, new TypeReference<>(){} - ); - - assertEquals( - preTransformedSpec.get("$schema").asText(), - "https://vega.github.io/schema/vega/v5.json" - ); - - // Collect list of warnings - JsonNode usermetaNode = preTransformedSpec.get("usermeta"); - JsonNode vegafusionWarningsNode = usermetaNode.get("vegafusion_warnings"); - assertTrue(vegafusionWarningsNode.isArray()); - List warningList = new ArrayList<>(); - var elements = vegafusionWarningsNode.elements(); - while (elements.hasNext()) { - warningList.add(elements.next()); - } - - // We should have 1 RowLimitExceeded warning - assertEquals(warningList.size(), 1); - var firstWarningType = warningList.get(0).get("type").asText(); - assertEquals(firstWarningType, "RowLimitExceeded"); - - // Clean up Runtime - runtime.destroy(); - - // Calling again after destroy should raise an exception - assertThrows(IllegalStateException.class, () -> - runtime.preTransformSpec( - spec, "UTC", "UTC", 3, true - ) - ); - } - - @Test - void testInvalidPretransformSpec() { - // Build VegaFusionRuntime - VegaFusionRuntime runtime = makeRuntime(); - - // Construct invalid Vega spec - String spec = "{\"data\": \"foo\"}"; - - // Check that exception is raised by preTransformSpec - VegaFusionException e = assertThrows(VegaFusionException.class, () -> - runtime.preTransformSpec( - spec, "UTC", "UTC", 0, true - ) - ); - - assertTrue(e.getMessage().toLowerCase().contains("serde")); - - // Clean up Runtime - runtime.destroy(); - } - - String histSpec() { - return """ - { - "$schema": "https://vega.github.io/schema/vega/v5.json", - "background": "white", - "description": "https://vega.github.io/vega-lite/examples/histogram.html", - "padding": 5, - "width": 200, - "height": 200, - "style": "cell", - "data": [ - { - "name": "source_0", - "url": "https://raw.githubusercontent.com/vega/vega-datasets/master/data/movies.json", - "format": {"type": "json"}, - "transform": [ - { - "type": "extent", - "field": "IMDB Rating", - "signal": "bin_maxbins_10_IMDB_Rating_extent" - }, - { - "type": "bin", - "field": "IMDB Rating", - "as": [ - "bin_maxbins_10_IMDB Rating", - "bin_maxbins_10_IMDB Rating_end" - ], - "signal": "bin_maxbins_10_IMDB_Rating_bins", - "extent": {"signal": "bin_maxbins_10_IMDB_Rating_extent"}, - "maxbins": 10 - }, - { - "type": "aggregate", - "groupby": [ - "bin_maxbins_10_IMDB Rating", - "bin_maxbins_10_IMDB Rating_end" - ], - "ops": ["count"], - "fields": [null], - "as": ["__count"] - }, - { - "type": "filter", - "expr": "isValid(datum[\\"bin_maxbins_10_IMDB Rating\\"]) && isFinite(+datum[\\"bin_maxbins_10_IMDB Rating\\"])" - } - ] - } - ], - "marks": [ - { - "name": "marks", - "type": "rect", - "style": ["bar"], - "from": {"data": "source_0"}, - "encode": { - "update": { - "fill": {"value": "#4c78a8"}, - "ariaRoleDescription": {"value": "bar"}, - "description": { - "signal": "\\"IMDB Rating (binned): \\" + (!isValid(datum[\\"bin_maxbins_10_IMDB Rating\\"]) || !isFinite(+datum[\\"bin_maxbins_10_IMDB Rating\\"]) ? \\"null\\" : format(datum[\\"bin_maxbins_10_IMDB Rating\\"], \\"\\") + \\" – \\" + format(datum[\\"bin_maxbins_10_IMDB Rating_end\\"], \\"\\")) + \\"; Count of Records: \\" + (format(datum[\\"__count\\"], \\"\\"))" - }, - "x2": [ - { - "test": "!isValid(datum[\\"bin_maxbins_10_IMDB Rating\\"]) || !isFinite(+datum[\\"bin_maxbins_10_IMDB Rating\\"])", - "value": 0 - }, - {"scale": "x", "field": "bin_maxbins_10_IMDB Rating", "offset": 1} - ], - "x": [ - { - "test": "!isValid(datum[\\"bin_maxbins_10_IMDB Rating\\"]) || !isFinite(+datum[\\"bin_maxbins_10_IMDB Rating\\"])", - "value": 0 - }, - {"scale": "x", "field": "bin_maxbins_10_IMDB Rating_end"} - ], - "y": {"scale": "y", "field": "__count"}, - "y2": {"scale": "y", "value": 0} - } - } - } - ], - "scales": [ - { - "name": "x", - "type": "linear", - "domain": { - "signal": "[bin_maxbins_10_IMDB_Rating_bins.start, bin_maxbins_10_IMDB_Rating_bins.stop]" - }, - "range": [0, {"signal": "width"}], - "bins": {"signal": "bin_maxbins_10_IMDB_Rating_bins"}, - "zero": false - }, - { - "name": "y", - "type": "linear", - "domain": {"data": "source_0", "field": "__count"}, - "range": [{"signal": "height"}, 0], - "nice": true, - "zero": true - } - ], - "axes": [ - { - "scale": "y", - "orient": "left", - "gridScale": "x", - "grid": true, - "tickCount": {"signal": "ceil(height/40)"}, - "domain": false, - "labels": false, - "aria": false, - "maxExtent": 0, - "minExtent": 0, - "ticks": false, - "zindex": 0 - }, - { - "scale": "x", - "orient": "bottom", - "grid": false, - "title": "IMDB Rating (binned)", - "labelFlush": true, - "labelOverlap": true, - "tickCount": {"signal": "ceil(width/10)"}, - "zindex": 0 - }, - { - "scale": "y", - "orient": "left", - "grid": false, - "title": "Count of Records", - "labelOverlap": true, - "tickCount": {"signal": "ceil(height/40)"}, - "zindex": 0 - } - ] - } - """; - } -} diff --git a/java/settings.gradle b/java/settings.gradle deleted file mode 100644 index d51a9a313..000000000 --- a/java/settings.gradle +++ /dev/null @@ -1,16 +0,0 @@ -/* - * This file was generated by the Gradle 'init' task. - * - * The settings file is used to specify which projects to include in your build. - * - * Detailed information about configuring a multi-project build in Gradle can be found - * in the user manual at https://docs.gradle.org/8.1.1/userguide/multi_project_builds.html - */ - -plugins { - // Apply the foojay-resolver plugin to allow automatic download of JDKs - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0' -} - -rootProject.name = 'VegaFusion' -include('lib') diff --git a/java/version.txt b/java/version.txt deleted file mode 100644 index e184d8477..000000000 --- a/java/version.txt +++ /dev/null @@ -1 +0,0 @@ -1.6.9 \ No newline at end of file diff --git a/pixi.toml b/pixi.toml index c98c4adf9..8f2c13844 100644 --- a/pixi.toml +++ b/pixi.toml @@ -77,32 +77,6 @@ build-rs-vegafusion-server = { cmd = "cargo build -p vegafusion-server --release # minio start-minio = "python automation/start_minio.py" -# Java -build-jni = "cargo build -p vegafusion-jni --release $0" -build-jar = "cd java && ./gradlew jar" -build-jar-win = "cd java && ./gradlew.bat jar" - -[tasks.test-java] -cmd = """ -export VEGAFUSION_JNI_LIBRARY=$(python automation/find_file.py $PIXI_PACKAGE_ROOT/target/release/ \"libvegafusion_jni\\.(so|dylib)$\") && -cd java && -./gradlew test $0 -""" -depends_on = ["build-jni"] - -[tasks.test-java-win] -cmd = """ -export VEGAFUSION_JNI_LIBRARY=$PIXI_PACKAGE_ROOT/target/release/vegafusion_jni.dll && -cd java && -./gradlew.bat test $0 -""" -depends_on = ["build-jni"] - -[tasks.publish-java] -cmd = """ -cd java/ && -./gradlew publish -""" # Note: the `--no-verify` flag below for `vegafusion-core` is due to this cargo publish error: # diff --git a/vegafusion-jni/Cargo.toml b/vegafusion-jni/Cargo.toml deleted file mode 100644 index d6c4c0f5d..000000000 --- a/vegafusion-jni/Cargo.toml +++ /dev/null @@ -1,37 +0,0 @@ -[package] -name = "vegafusion-jni" -version = "1.6.9" -edition = "2021" - -[lib] -crate-type = ["cdylib"] - -[features] -protobuf-src = ["vegafusion-core/protobuf-src"] - -[dependencies] -jni = "0.21.1" -serde_json = "1.0.96" - -[dependencies.vegafusion-common] -path = "../vegafusion-common" -version = "1.6.9" -features = ["jni"] - -[dependencies.vegafusion-core] -path = "../vegafusion-core" -features = ["tonic_support"] -version = "1.6.9" - -[dependencies.vegafusion-runtime] -path = "../vegafusion-runtime" -version = "1.6.9" - -[dependencies.vegafusion-sql] -path = "../vegafusion-sql" -version = "1.6.9" -features = ["datafusion-conn"] - -[dependencies.tokio] -workspace = true -features = ["macros", "rt-multi-thread"] diff --git a/vegafusion-jni/README.md b/vegafusion-jni/README.md deleted file mode 100644 index a7ef3dc60..000000000 --- a/vegafusion-jni/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# vegafusion-jni -This crate uses the [jni-rs](https://github.com/jni-rs/jni-rs) crate to provide a Java JNI interface to VegaFusion. This crate is pure Rust and builds the shared library that is loaded by the pure Java logic in the [java](../java) directory. \ No newline at end of file diff --git a/vegafusion-jni/src/lib.rs b/vegafusion-jni/src/lib.rs deleted file mode 100644 index 5c48b28e5..000000000 --- a/vegafusion-jni/src/lib.rs +++ /dev/null @@ -1,329 +0,0 @@ -use std::sync::Arc; -// This is the interface to the JVM that we'll call the majority of our -// methods on. -use jni::JNIEnv; - -// These objects are what you should use as arguments to your native -// function. They carry extra lifetime information to prevent them escaping -// this context and getting used after being GC'd. -use jni::objects::{JClass, JObject, JString}; - -// This is just a pointer. We'll be returning it from our function. We -// can't return one of the objects with lifetime information because the -// lifetime checker won't let us. -use jni::sys::{jboolean, jint, jlong, jobject, jstring}; -use std::panic; -use vegafusion_common::error::Result; -use vegafusion_core::patch::patch_pre_transformed_spec; -use vegafusion_core::planning::plan::PreTransformSpecWarningSpec; -use vegafusion_core::spec::chart::ChartSpec; -use vegafusion_runtime::task_graph::runtime::VegaFusionRuntime; -use vegafusion_sql::connection::datafusion_conn::DataFusionConnection; -use vegafusion_sql::connection::Connection; - -struct VegaFusionRuntimeState { - pub vf_runtime: VegaFusionRuntime, - pub tokio_runtime: tokio::runtime::Runtime, -} - -#[no_mangle] -pub extern "system" fn Java_io_vegafusion_VegaFusionRuntime_version<'local>( - mut env: JNIEnv<'local>, - _class: JClass<'local>, -) -> jstring { - match inner_version(&env) { - Ok(version) => version, - Err(err) => { - let _ = env.throw_new("io/vegafusion/VegaFusionException", err.to_string()); - JObject::null().into_raw() - } - } -} - -fn inner_version(env: &JNIEnv) -> Result { - let version = env!("CARGO_PKG_VERSION"); - let output = env.new_string(version)?; - Ok(output.into_raw()) -} - -#[no_mangle] -pub extern "system" fn Java_io_vegafusion_VegaFusionRuntime_innerCreate<'local>( - mut env: JNIEnv<'local>, - _class: JClass<'local>, - capacity: jlong, - memory_limit: jlong, - num_threads: jint, -) -> jlong { - let result = panic::catch_unwind(|| inner_create(capacity, memory_limit, num_threads)); - - match result { - Ok(Ok(state)) => Box::into_raw(Box::new(state)) as jlong, - Ok(Err(vf_err)) => { - let _ = env.throw_new("io/vegafusion/VegaFusionException", vf_err.to_string()); - 0 - } - Err(_unwind_err) => { - let _ = env.throw_new("io/vegafusion/VegaFusionException", "Uncaught Error"); - 0 - } - } -} - -fn inner_create( - capacity: jlong, - memory_limit: jlong, - num_threads: jint, -) -> Result { - // Use DataFusion connection and multi-threaded tokio runtime - let conn = Arc::new(DataFusionConnection::default()) as Arc; - let capacity = if capacity < 1 { - None - } else { - Some(capacity as usize) - }; - let memory_limit = if memory_limit < 1 { - None - } else { - Some(memory_limit as usize) - }; - let vf_runtime = VegaFusionRuntime::new(conn, capacity, memory_limit); - - // Build tokio runtime - let mut builder = tokio::runtime::Builder::new_multi_thread(); - builder.enable_all(); - builder.worker_threads(num_threads.max(1) as usize); - let tokio_runtime = builder.build()?; - - Ok(VegaFusionRuntimeState { - vf_runtime, - tokio_runtime, - }) -} - -/// # Safety -/// This function uses the unsafe Box::from_raw function to convert the state pointer -/// to a Boxed VegaFusionRuntimeState so that it will be dropped -#[no_mangle] -pub unsafe extern "system" fn Java_io_vegafusion_VegaFusionRuntime_innerDestroy<'local>( - _env: JNIEnv<'local>, - _class: JClass<'local>, - state_ptr: jlong, -) { - // Cast/Box the state_ptr so that the drop logic will run - let _boxed_state = Box::from_raw(state_ptr as *mut VegaFusionRuntimeState); -} - -#[no_mangle] -pub extern "system" fn Java_io_vegafusion_VegaFusionRuntime_innerPatchPreTransformedSpec<'local>( - mut env: JNIEnv<'local>, - class: JClass<'local>, - spec1: JString<'local>, - pre_transformed_spec1: JString<'local>, - spec2: JString<'local>, -) -> jstring { - if let Ok((spec1, pre_transformed_spec1, spec2)) = - parse_args_patch_pre_transformed_spec(&mut env, class, spec1, pre_transformed_spec1, spec2) - { - let result = panic::catch_unwind(|| { - inner_patch_pre_transformed_spec(&spec1, &pre_transformed_spec1, &spec2) - }); - - match result { - Ok(Ok(Some(patched_spec))) => match env.new_string(patched_spec) { - Ok(patched_spec) => patched_spec.into_raw(), - Err(err) => { - let _ = env.throw_new("io/vegafusion/VegaFusionException", err.to_string()); - JObject::null().into_raw() - } - }, - Ok(Ok(None)) => { - // Patch ran without error, but not patch result was found, return null - JObject::null().into_raw() - } - Ok(Err(vf_err)) => { - let _ = env.throw_new("io/vegafusion/VegaFusionException", vf_err.to_string()); - JObject::null().into_raw() - } - Err(_unwind_err) => { - let _ = env.throw_new("io/vegafusion/VegaFusionException", "Uncaught Error"); - JObject::null().into_raw() - } - } - } else { - let _ = env.throw_new( - "io/vegafusion/VegaFusionException", - "Failed to parse args to innerPatchPreTransformedSpec", - ); - JObject::null().into_raw() - } -} - -pub fn parse_args_patch_pre_transformed_spec<'local>( - env: &mut JNIEnv<'local>, - _class: JClass<'local>, - spec1: JString<'local>, - pre_transformed_spec1: JString<'local>, - spec2: JString<'local>, -) -> Result<(String, String, String)> { - let spec1: String = env.get_string(&spec1)?.into(); - - let pre_transformed_spec1: String = env.get_string(&pre_transformed_spec1)?.into(); - - let spec2: String = env.get_string(&spec2)?.into(); - - Ok((spec1, pre_transformed_spec1, spec2)) -} - -pub fn inner_patch_pre_transformed_spec( - spec1: &str, - pre_transformed_spec1: &str, - spec2: &str, -) -> Result> { - // Parse specs - let spec1: ChartSpec = serde_json::from_str(spec1)?; - let pre_transformed_spec1: ChartSpec = serde_json::from_str(pre_transformed_spec1)?; - let spec2: ChartSpec = serde_json::from_str(spec2)?; - - let pre_transformed_spec2 = patch_pre_transformed_spec(&spec1, &pre_transformed_spec1, &spec2)?; - - if let Some(pre_transformed_spec2) = pre_transformed_spec2 { - Ok(Some(serde_json::to_string(&pre_transformed_spec2)?)) - } else { - // Return null - Ok(None) - } -} - -struct PreTransformSpecArgs { - spec: String, - local_tz: String, - default_input_tz: Option, - row_limit: Option, - preserve_interactivity: bool, -} - -fn parse_args_pre_transform_spec<'local>( - env: &mut JNIEnv<'local>, - spec: JString<'local>, - local_tz: JString<'local>, - default_input_tz: JString<'local>, - row_limit: jint, - preserve_interactivity: jboolean, -) -> Result { - let spec: String = env.get_string(&spec)?.into(); - - let local_tz: String = if local_tz.is_null() { - "UTC".to_string() - } else { - env.get_string(&local_tz)?.into() - }; - - // default_input_tz - let default_input_tz: Option = if default_input_tz.is_null() { - None - } else { - Some(env.get_string(&default_input_tz)?.into()) - }; - - let row_limit = if row_limit < 1 { - None - } else { - Some(row_limit as u32) - }; - - Ok(PreTransformSpecArgs { - spec, - local_tz, - default_input_tz, - row_limit, - preserve_interactivity: preserve_interactivity != 0, - }) -} - -/// # Safety -/// This function performs an unsafe cast of the pointer to a VegaFusionRuntimeState reference -unsafe fn inner_pre_transform_spec(pointer: jlong, args: PreTransformSpecArgs) -> Result { - let state = &*(pointer as *const VegaFusionRuntimeState); - let spec: ChartSpec = serde_json::from_str(args.spec.as_str())?; - - let (mut pre_transformed_spec, warnings) = - state - .tokio_runtime - .block_on(state.vf_runtime.pre_transform_spec( - &spec, - args.local_tz.as_str(), - &args.default_input_tz, - args.row_limit, - args.preserve_interactivity, - Default::default(), - Default::default(), - ))?; - - // Convert warnings to JSON compatible PreTransformSpecWarningSpec - let warnings: Vec<_> = warnings - .iter() - .map(PreTransformSpecWarningSpec::from) - .collect(); - - // Add warnings to usermeta - pre_transformed_spec.usermeta.insert( - "vegafusion_warnings".to_string(), - serde_json::to_value(warnings)?, - ); - - let pre_transformed_spec = serde_json::to_string(&pre_transformed_spec)?; - Ok(pre_transformed_spec) -} - -/// # Safety -/// This function calls inner_pre_transform_spec which performs an unsafe cast of the pointer -/// to a VegaFusionRuntimeState reference -#[no_mangle] -pub unsafe extern "system" fn Java_io_vegafusion_VegaFusionRuntime_innerPreTransformSpec<'local>( - mut env: JNIEnv<'local>, - _class: JClass<'local>, - pointer: jlong, - spec: JString<'local>, - local_tz: JString<'local>, - default_input_tz: JString<'local>, - row_limit: jint, - preserve_interactivity: jboolean, -) -> jobject { - if let Ok(args) = parse_args_pre_transform_spec( - &mut env, - spec, - local_tz, - default_input_tz, - row_limit, - preserve_interactivity, - ) { - let result = panic::catch_unwind(|| inner_pre_transform_spec(pointer, args)); - - match result { - Ok(Ok(pre_transformed_spec)) => { - let pre_transformed_spec = match env.new_string(pre_transformed_spec) { - Ok(pre_transformed_spec) => JObject::from(pre_transformed_spec), - Err(err) => { - let _ = env.throw_new("io/vegafusion/VegaFusionException", err.to_string()); - return JObject::null().into_raw(); - } - }; - pre_transformed_spec.into_raw() - } - Ok(Err(vf_err)) => { - let _ = env.throw_new("io/vegafusion/VegaFusionException", vf_err.to_string()); - JObject::null().into_raw() - } - Err(_unwind_err) => { - let _ = env.throw_new("io/vegafusion/VegaFusionException", "Uncaught Error"); - JObject::null().into_raw() - } - } - } else { - let _ = env.throw_new( - "io/vegafusion/VegaFusionException", - "Failed to parse args to innerPreTransformSpec", - ); - JObject::null().into_raw() - } -}