Add the Java binding over the C ABI hub (Panama/FFM) (#233)
Adds a Java binding (`bindings/java`) over the C ABI hub — the fourth language stecker after C#, Go and R, reaching the hub through the Java Foreign Function & Memory API (Panama, `java.lang.foreign`, final in Java 22) rather than JNI or jextract. ## What's here - **`bindings/java`** — a Maven module (`org.wickra:wickra`) exposing all 514 indicators as idiomatic `AutoCloseable` classes. The downcall handles (`internal/NativeMethods.java`), the per-indicator wrappers and the output records are generated from `bindings/c/include/wickra.h` (same eight-archetype taxonomy as the C#/Go/R generators: scalar/batch, multi-output, bars, profile, values-profile, array-input). The opaque handle is a `MemorySegment` freed by a registered `java.lang.ref.Cleaner` action; multi-output returns a `record` (`null` at warmup), bars a `record[]`, profiles a record with a trailing `double[]`. The hand-written `WickraNative` resolves the native library (a bundled per-platform copy, or a `target/release` fallback for local development) and validates it against a sentinel symbol. repr(C) struct offsets are computed in the generator so the FFM reads land on the exact bytes. - **`examples/java`** — the full example suite mirroring C/C#/Go/R: streaming, backtest, multi_timeframe, parallel_assets (parallel streams), three strategies, and `FetchBtcusdt`/`LiveBinance`. - **CI** — a `java` job builds the C ABI library, sets up JDK 22 (Temurin, with a CDN-flake retry), runs the archetype test suite and the seven offline examples on Linux, macOS and Windows. - **Release** — a gated `java-publish` job (skipped until the `JAVA_PUBLISH_ENABLED` repository variable is set) stages the native libraries from the `wickra-c-<triple>.tar.gz` assets into the binding's resources and deploys to Maven Central with GPG signing. Independent of the GitHub-release job, like the NuGet job. - **Docs** — Java added to the README languages table, project layout, building/testing and comparison table, CONTRIBUTING, ARCHITECTURE, the examples index, the issue/PR templates, the About-description template, and the other binding READMEs. ## Requirements Java 22+ (the FFM API is final since Java 22). The binding requires `--enable-native-access=ALL-UNNAMED` at runtime; the test and example runners pass it automatically. No Rust crate or `Cargo.toml` change — the Java binding is standalone and additive. The generated `*.java` are committed (like the node `index.js`/`index.d.ts`); the generator stays private.
This commit is contained in:
@@ -30,7 +30,7 @@ assignees: ""
|
||||
## Environment
|
||||
|
||||
- Wickra version:
|
||||
- Language / binding: <!-- Rust crate / Python / Node / WASM / C ABI / C# (.NET) / Go / R -->
|
||||
- Language / binding: <!-- Rust crate / Python / Node / WASM / C ABI / C# (.NET) / Go / Java / R -->
|
||||
- OS and architecture:
|
||||
- Rust / Python / Node / .NET version (If relevant):
|
||||
|
||||
|
||||
@@ -16,6 +16,11 @@ assignees: []
|
||||
- [ ] Python (`pip install wickra`)
|
||||
- [ ] Node.js (`npm install wickra`)
|
||||
- [ ] WebAssembly
|
||||
- [ ] C ABI (`bindings/c`)
|
||||
- [ ] C# / .NET (`Wickra` on NuGet)
|
||||
- [ ] Go (`bindings/go`)
|
||||
- [ ] Java (`org.wickra:wickra` on Maven Central)
|
||||
- [ ] R (`bindings/r`)
|
||||
- [ ] Docs / examples only
|
||||
|
||||
## Environment
|
||||
|
||||
@@ -28,6 +28,7 @@ assignees: ""
|
||||
- [ ] Should be exposed in the C ABI
|
||||
- [ ] Should be exposed in the C# / .NET binding
|
||||
- [ ] Should be exposed in the Go binding
|
||||
- [ ] Should be exposed in the Java binding
|
||||
- [ ] Should be exposed in the R binding
|
||||
|
||||
## Additional context
|
||||
|
||||
@@ -13,7 +13,7 @@ assignees: []
|
||||
## Affected code path
|
||||
|
||||
- Indicator / API: `e.g. EMA.update`
|
||||
- Binding: `Rust / Python / Node / Wasm / C ABI / C# (.NET) / Go / R`
|
||||
- Binding: `Rust / Python / Node / Wasm / C ABI / C# (.NET) / Go / Java / R`
|
||||
- Hot loop or one-shot call?
|
||||
|
||||
## Versions compared
|
||||
|
||||
@@ -33,4 +33,4 @@ import wickra as ta
|
||||
## Environment (Only if relevant)
|
||||
|
||||
- Wickra version: `e.g. 0.4.2`
|
||||
- Binding: `Rust / Python / Node / Wasm / C ABI / C# (.NET) / Go / R`
|
||||
- Binding: `Rust / Python / Node / Wasm / C ABI / C# (.NET) / Go / Java / R`
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
- [ ] `cargo clippy --workspace --all-targets -- -D warnings` is clean.
|
||||
- [ ] `cargo test --workspace` passes.
|
||||
- [ ] New behaviour has tests; bug fixes have a regression test.
|
||||
- [ ] Public API changes are mirrored in the Python / Node / WASM bindings, and the C ABI + C# + Go + R bindings are regenerated
|
||||
- [ ] Public API changes are mirrored in the Python / Node / WASM bindings, and the C ABI + C# + Go + Java + R bindings are regenerated
|
||||
and their type stubs (If applicable).
|
||||
- [ ] The relevant page on the [documentation site](https://docs.wickra.org)
|
||||
and the `README.md` are updated (If applicable). Docs edits go to a
|
||||
|
||||
@@ -26,6 +26,7 @@ Please fill in the sections below. Delete any that don't apply.
|
||||
- [ ] C ABI (`bindings/c`)
|
||||
- [ ] C# / .NET binding (`bindings/csharp`)
|
||||
- [ ] Go binding (`bindings/go`)
|
||||
- [ ] Java binding (`bindings/java`)
|
||||
- [ ] R binding (`bindings/r`)
|
||||
- [ ] Examples / docs
|
||||
|
||||
|
||||
@@ -896,6 +896,77 @@ jobs:
|
||||
- name: Parse the network R examples
|
||||
run: Rscript -e 'invisible(lapply(c("examples/r/fetch_btcusdt.R", "examples/r/live_binance.R"), parse)); cat("network R examples parse OK\n")'
|
||||
|
||||
java:
|
||||
name: Java on ${{ matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable branch, 2026-03-27
|
||||
|
||||
- name: Cache cargo
|
||||
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
|
||||
continue-on-error: true # cache is an optimisation; never block on a stuck/slow restore
|
||||
timeout-minutes: 6
|
||||
|
||||
# The binding links the C ABI hub at runtime through the Java FFM API; build
|
||||
# it first so WickraNative's development fallback finds
|
||||
# target/release/wickra.{so,dylib,dll}. JDK 22+ is required for the final FFM
|
||||
# API, so it is installed explicitly (runners ship 17/21).
|
||||
- name: Build the C ABI library
|
||||
run: cargo build -p wickra-c --release
|
||||
|
||||
- name: Set up JDK 22
|
||||
id: setup-java
|
||||
continue-on-error: true
|
||||
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "22"
|
||||
cache: maven
|
||||
|
||||
- name: Retry JDK setup (CDN flake)
|
||||
if: steps.setup-java.outcome == 'failure'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "::warning::setup-java failed (likely CDN flake), waiting 30s before retry..."
|
||||
sleep 30
|
||||
|
||||
- name: Set up JDK 22 (retry)
|
||||
if: steps.setup-java.outcome == 'failure'
|
||||
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "22"
|
||||
cache: maven
|
||||
|
||||
- name: Java info
|
||||
run: java -version
|
||||
|
||||
# `install` runs the archetype test suite (the real FFI boundary check) and
|
||||
# installs the binding to the local repo so the examples can resolve it.
|
||||
- name: Test and install the Java binding
|
||||
run: mvn -B -f bindings/java install
|
||||
|
||||
- name: Build the Java examples
|
||||
run: mvn -B -f examples/java compile
|
||||
|
||||
# Run only the offline examples (fetch_btcusdt / live_binance need network).
|
||||
- name: Run the offline Java examples
|
||||
shell: bash
|
||||
run: |
|
||||
for cls in Streaming Backtest MultiTimeframe ParallelAssets \
|
||||
StrategyRsiMeanReversion StrategyMacdAdx StrategyBollingerSqueeze; do
|
||||
mvn -B -q -f examples/java exec:exec -Dexec.mainClass="org.wickra.examples.$cls"
|
||||
done
|
||||
|
||||
# The cross-library benchmark has moved to a dedicated scheduled workflow
|
||||
# (.github/workflows/bench.yml) — see audit finding R10. It runs nightly
|
||||
# at 03:00 UTC and on-demand via `workflow_dispatch`, and is no longer on
|
||||
|
||||
@@ -706,6 +706,75 @@ jobs:
|
||||
path: nupkg/*.nupkg
|
||||
if-no-files-found: error
|
||||
|
||||
# Publish the Java binding to Maven Central. Independent of the GitHub-release
|
||||
# job so a Java hiccup never blocks the C/C++ asset release. The Maven Central
|
||||
# namespace (org.wickra) is verified; authentication uses the Sonatype Central
|
||||
# Portal token (CENTRAL_USERNAME / CENTRAL_PASSWORD) and artifacts are signed
|
||||
# with the GPG key (GPG_PRIVATE_KEY / GPG_PASSPHRASE). The pom version must
|
||||
# match the release tag (kept in sync by the version-bump checklist).
|
||||
java-publish:
|
||||
name: Publish to Maven Central
|
||||
needs: c-abi-build
|
||||
runs-on: ubuntu-latest
|
||||
environment: release
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Download the C ABI native libraries
|
||||
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
||||
with:
|
||||
pattern: c-abi-*
|
||||
path: c-abi-artifacts
|
||||
|
||||
- name: Stage native libraries into resources/native/<os>-<arch>
|
||||
shell: bash
|
||||
run: |
|
||||
set -e
|
||||
declare -A PLAT=(
|
||||
[x86_64-unknown-linux-gnu]=linux-x64
|
||||
[aarch64-unknown-linux-gnu]=linux-arm64
|
||||
[x86_64-apple-darwin]=osx-x64
|
||||
[aarch64-apple-darwin]=osx-arm64
|
||||
[x86_64-pc-windows-msvc]=win-x64
|
||||
[aarch64-pc-windows-msvc]=win-arm64
|
||||
)
|
||||
base=bindings/java/src/main/resources/native
|
||||
for target in "${!PLAT[@]}"; do
|
||||
archive=$(find c-abi-artifacts -name "wickra-c-$target.tar.gz" | head -1)
|
||||
if [ -z "$archive" ]; then
|
||||
echo "::error::missing native artifact for $target"; exit 1
|
||||
fi
|
||||
tmp=$(mktemp -d); tar -xzf "$archive" -C "$tmp"
|
||||
dest="$base/${PLAT[$target]}"; mkdir -p "$dest"
|
||||
for f in libwickra.so libwickra.dylib wickra.dll; do
|
||||
src=$(find "$tmp" -name "$f" | head -1)
|
||||
[ -n "$src" ] && cp "$src" "$dest/"
|
||||
done
|
||||
echo "staged ${PLAT[$target]}:"; ls -l "$dest"
|
||||
done
|
||||
|
||||
# setup-java writes a settings.xml with the 'central' server credentials
|
||||
# (mapped from the env vars below) and imports the GPG signing key.
|
||||
- name: Set up JDK 22 + Maven Central credentials + GPG
|
||||
uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: "22"
|
||||
server-id: central
|
||||
server-username: CENTRAL_USERNAME
|
||||
server-password: CENTRAL_PASSWORD
|
||||
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
|
||||
gpg-passphrase: GPG_PASSPHRASE
|
||||
|
||||
- name: Deploy to Maven Central
|
||||
env:
|
||||
CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
|
||||
CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: mvn -B -f bindings/java -Prelease deploy -DskipTests
|
||||
|
||||
github-release:
|
||||
name: Attach assets to the draft GitHub Release
|
||||
needs: [cargo-publish, python-publish, node-publish, wasm-publish, c-abi-build]
|
||||
|
||||
@@ -156,7 +156,7 @@ jobs:
|
||||
# actually live (Cloudflare Pages, P8.1); merging this PR is therefore
|
||||
# gated on the domain resolving, otherwise the About link would 404.
|
||||
homepage="https://docs.wickra.org"
|
||||
desc="Streaming-first technical indicators with a Rust core and Python, Node.js, WebAssembly, C ABI, .NET, Go, and R bindings. ${n} indicators, O(1) per-tick updates, no system dependencies. Drop-in TA-Lib replacement."
|
||||
desc="Streaming-first technical indicators with a Rust core and Python, Node.js, WebAssembly, C ABI, .NET, Go, Java, and R bindings. ${n} indicators, O(1) per-tick updates, no system dependencies. Drop-in TA-Lib replacement."
|
||||
# Enforce the homepage unconditionally — it is a constant, so this both
|
||||
# corrects the stale kingchenc URL and self-heals any future drift.
|
||||
# Same Administration-write permission as --description (no extra scope).
|
||||
|
||||
Reference in New Issue
Block a user