Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .github/workflows/ci-java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,31 @@ jobs:
working-directory: sdks/java
shell: bash
run: ./gradlew build --no-daemon

# Prove the SDK works under GraalVM native-image: run the smoke under the
# tracing agent to collect JNI/reflection/resource metadata, then build a
# native image with it and run the binary.
graalvm:
name: Java SDK GraalVM native-image
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v7

- name: Set up Rust
uses: ./.github/actions/setup-rust

- name: Set up GraalVM
uses: graalvm/setup-graalvm@v1
with:
java-version: "21"
distribution: graalvm
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Collect metadata, build, and run the native image
working-directory: sdks/java
run: |
./gradlew -Pagent :graalvm-smoke:run --no-daemon
./gradlew :graalvm-smoke:metadataCopy --no-daemon
./gradlew :graalvm-smoke:nativeCompile --no-daemon
./graalvm-smoke/build/native/nativeCompile/graalvm-smoke
3 changes: 3 additions & 0 deletions sdks/java/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
.gradle/
build/
bin/

# Agent-generated GraalVM metadata for the smoke (regenerated in CI).
graalvm-smoke/src/main/resources/META-INF/native-image/
57 changes: 57 additions & 0 deletions sdks/java/graalvm-smoke/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// A throwaway consumer that drives the SDK's JNI + Jackson paths end to end.
// CI builds it into a GraalVM native image to verify the reachability metadata
// the runtime ships. Not published; not part of the SDK artifact.
plugins {
application
checkstyle
id("com.diffplug.spotless") version "6.25.0"
id("org.graalvm.buildtools.native") version "0.10.6"
}

group = "org.byteveda"
version = "0.16.4"

repositories {
mavenCentral()
}

dependencies {
implementation(project(":"))
}

application {
mainClass.set("org.byteveda.taskito.graalvm.Smoke")
}

graalvmNative {
binaries.named("main") {
// Fail the build on missing metadata rather than emitting a fallback image.
buildArgs.add("--no-fallback")
}
// CI runs the smoke under the tracing agent (`-Pagent :graalvm-smoke:run`)
// then `metadataCopy` to populate this module's resources, so `nativeCompile`
// sees the JNI/reflection metadata the SDK's JNI + Jackson paths require.
agent {
metadataCopy {
inputTaskNames.add("run")
outputDirectories.add("src/main/resources/META-INF/native-image")
mergeWithExisting.set(true)
}
}
}

spotless {
java {
target("src/**/*.java")
palantirJavaFormat("2.50.0")
removeUnusedImports()
trimTrailingWhitespace()
endWithNewline()
}
}

checkstyle {
toolVersion = "10.21.4"
configFile = file("../config/checkstyle/checkstyle.xml")
isIgnoreFailures = false
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package org.byteveda.taskito.graalvm;

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.byteveda.taskito.Queue;
import org.byteveda.taskito.Taskito;
import org.byteveda.taskito.events.EventName;
import org.byteveda.taskito.scheduling.PeriodicTask;
import org.byteveda.taskito.task.Task;
import org.byteveda.taskito.worker.Worker;

/**
* Drives the SDK's JNI dispatch and Jackson (de)serialization end to end so a
* GraalVM native image of this class proves the shipped reachability metadata is
* complete. Exits non-zero on any failure.
*/
public final class Smoke {

private Smoke() {}

public static void main(String[] args) throws Exception {
Path dir = Files.createTempDirectory("taskito-graalvm-smoke");
Task<String> echo = Task.of("echo", String.class);

try (Queue queue = Taskito.builder()
.backend("sqlite")
.url(dir.resolve("smoke.db").toString())
.open()) {
String id = queue.enqueue(echo, "graalvm");

CountDownLatch done = new CountDownLatch(1);
try (Worker worker = queue.worker()
.handle(echo, (String payload) -> payload.length())
.on(EventName.SUCCESS, event -> done.countDown())
.start()) {
if (!done.await(20, TimeUnit.SECONDS)) {
throw new IllegalStateException("task did not complete");
}
}

byte[] result = queue.getResult(id).orElseThrow(() -> new IllegalStateException("no result persisted"));
String value = new String(result, StandardCharsets.UTF_8);
if (!"7".equals(value)) {
throw new IllegalStateException("unexpected result: " + value);
}

// Exercise the Jackson DTO paths (PeriodicInfo + DeadJob deserialization).
queue.registerPeriodic(
PeriodicTask.builder("nightly", "echo", "0 0 0 * * *").build());
if (queue.listPeriodic().isEmpty()) {
throw new IllegalStateException("listPeriodic returned empty");
}
queue.listDead(10, 0);

System.out.println("taskito graalvm smoke ok");
}
}
}
1 change: 1 addition & 0 deletions sdks/java/settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ rootProject.name = "taskito"

include(":processor")
include(":test-support")
include(":graalvm-smoke")