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:
kingchenc
2026-06-09 20:30:29 +02:00
committed by GitHub
parent 8659b42bef
commit 167f7b3ffe
664 changed files with 44991 additions and 26 deletions
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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`
+1 -1
View File
@@ -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
+71
View File
@@ -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
+69
View File
@@ -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]
+1 -1
View File
@@ -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).