{"id":45591739,"url":"https://github.com/openjobspec/ojs-java-sdk","last_synced_at":"2026-04-20T19:01:44.528Z","repository":{"id":338689227,"uuid":"1156987853","full_name":"openjobspec/ojs-java-sdk","owner":"openjobspec","description":"The official Java SDK for Open Job Spec (OJS), a language-agnostic standard for background job processing.","archived":false,"fork":false,"pushed_at":"2026-03-09T20:18:14.000Z","size":295,"stargazers_count":0,"open_issues_count":15,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-10T01:57:18.029Z","etag":null,"topics":["background-jobs","java","job-queue","ojs","openjobspec","sdk","worker"],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openjobspec.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-13T09:43:28.000Z","updated_at":"2026-03-09T20:16:00.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/openjobspec/ojs-java-sdk","commit_stats":null,"previous_names":["openjobspec/ojs-java-sdk"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/openjobspec/ojs-java-sdk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openjobspec%2Fojs-java-sdk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openjobspec%2Fojs-java-sdk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openjobspec%2Fojs-java-sdk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openjobspec%2Fojs-java-sdk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openjobspec","download_url":"https://codeload.github.com/openjobspec/ojs-java-sdk/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openjobspec%2Fojs-java-sdk/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32061251,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T11:35:06.609Z","status":"ssl_error","status_checked_at":"2026-04-20T11:34:48.899Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["background-jobs","java","job-queue","ojs","openjobspec","sdk","worker"],"created_at":"2026-02-23T12:42:11.122Z","updated_at":"2026-04-20T19:01:44.458Z","avatar_url":"https://github.com/openjobspec.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OJS Java SDK\n[![Stability: stable](https://img.shields.io/badge/stability-stable-brightgreen.svg)](https://github.com/openjobspec/openjobspec/blob/main/STABILITY.md)\n\n[![CI](https://github.com/openjobspec/ojs-java-sdk/actions/workflows/ci.yml/badge.svg)](https://github.com/openjobspec/ojs-java-sdk/actions/workflows/ci.yml)\n[![Coverage](https://github.com/openjobspec/ojs-java-sdk/raw/main/.github/badges/jacoco.svg)](https://github.com/openjobspec/ojs-java-sdk/actions/workflows/ci.yml)\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Java](https://img.shields.io/badge/Java-21%2B-orange)](https://openjdk.org/projects/jdk/21/)\n\nOfficial [Open Job Spec](https://openjobspec.org) SDK for Java 21+.\n\n\u003e **🚀 Try it now:** [Open in Playground](https://playground.openjobspec.org?lang=java) · [Run on CodeSandbox](https://codesandbox.io/p/sandbox/openjobspec-java-quickstart) · [Docker Quickstart](https://github.com/openjobspec/openjobspec/blob/main/docker-compose.quickstart.yml)\n\n## Features\n\n- **Java 21+** -- records, sealed interfaces, virtual threads (Project Loom)\n- **Zero dependencies** -- uses only `java.net.http.HttpClient` from the standard library\n- **Optional Jackson support** -- Jackson annotations on types for enhanced JSON serialization\n- **Builder pattern** -- fluent builders for all configuration\n- **Virtual threads** -- concurrent worker execution with Project Loom\n- **Full OJS support** -- enqueue, workers, middleware, workflows (chain/group/batch)\n\n## Installation\n\n### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.openjobspec\u003c/groupId\u003e\n    \u003cartifactId\u003eojs-sdk\u003c/artifactId\u003e\n    \u003cversion\u003e0.3.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```kotlin\nimplementation(\"org.openjobspec:ojs-sdk:0.3.0\")\n```\n\n## Quick Start\n\n### Client (Producer)\n\n```java\nimport org.openjobspec.ojs.*;\nimport java.time.Duration;\nimport java.util.*;\n\nvar client = OJSClient.builder()\n    .url(\"http://localhost:8080\")\n    .build();\n\n// Simple enqueue\nvar job = client.enqueue(\"email.send\", Map.of(\"to\", \"user@example.com\"));\n\n// Enqueue with options\nvar job = client.enqueue(\"report.generate\", (Object) Map.of(\"id\", 42))\n    .queue(\"reports\")\n    .delay(Duration.ofMinutes(5))\n    .retry(RetryPolicy.builder().maxAttempts(5).build())\n    .unique(UniquePolicy.builder()\n        .key(List.of(\"id\"))\n        .period(Duration.ofHours(1))\n        .build())\n    .send();\n```\n\n### Worker (Consumer)\n\n```java\nvar worker = OJSWorker.builder()\n    .url(\"http://localhost:8080\")\n    .queues(List.of(\"default\", \"email\"))\n    .concurrency(10)\n    .build();\n\nworker.register(\"email.send\", ctx -\u003e {\n    var to = (String) ctx.job().argsMap().get(\"to\");\n    sendEmail(to);\n    return Map.of(\"messageId\", \"...\");\n});\n\n// Middleware\nworker.use((ctx, next) -\u003e {\n    System.out.printf(\"Processing %s%n\", ctx.job().type());\n    var start = Instant.now();\n    next.handle(ctx);\n    System.out.printf(\"Done in %dms%n\",\n        Duration.between(start, Instant.now()).toMillis());\n});\n\n// Graceful shutdown\nRuntime.getRuntime().addShutdownHook(new Thread(worker::stop));\nworker.start();\n```\n\n### Workflows\n\nOJS provides three workflow primitives — **chain** (sequential), **group** (parallel fan-out/fan-in), and **batch** (parallel with callbacks):\n\n```mermaid\ngraph LR\n    subgraph Chain\n    A1[Step 1] --\u003e A2[Step 2] --\u003e A3[Step 3]\n    end\n```\n```mermaid\ngraph TD\n    subgraph Group\n    S[Start] --\u003e G1[Task A] \u0026 G2[Task B] \u0026 G3[Task C] --\u003e J[All Complete]\n    end\n```\n\n```java\n// Chain (sequential)\nvar chain = Workflow.chain(\"order-processing\",\n    Workflow.step(\"order.validate\", Map.of(\"order_id\", \"ord_123\")),\n    Workflow.step(\"payment.charge\", Map.of()),\n    Workflow.step(\"notification.send\", Map.of())\n);\nclient.createWorkflow(chain);\n\n// Group (parallel)\nvar group = Workflow.group(\"multi-export\",\n    Workflow.step(\"export.csv\", Map.of(\"report_id\", \"rpt_456\")),\n    Workflow.step(\"export.pdf\", Map.of(\"report_id\", \"rpt_456\"))\n);\nclient.createWorkflow(group);\n\n// Batch (parallel with callbacks)\nvar batch = Workflow.batch(\"bulk-email\",\n    Workflow.callbacks()\n        .onComplete(Workflow.step(\"batch.report\", Map.of()))\n        .onFailure(Workflow.step(\"batch.alert\", Map.of())),\n    Workflow.step(\"email.send\", Map.of(\"to\", \"user1@example.com\")),\n    Workflow.step(\"email.send\", Map.of(\"to\", \"user2@example.com\"))\n);\nclient.createWorkflow(batch);\n```\n\n## API Reference\n\n### Core Types\n\n| Type | Description |\n|------|-------------|\n| `Job` | Job envelope record with all OJS attributes |\n| `RetryPolicy` | Retry configuration (max attempts, backoff, jitter) |\n| `UniquePolicy` | Deduplication policy (key, period, conflict resolution) |\n| `JobContext` | Context passed to handlers (job, result, heartbeat) |\n| `JobHandler` | `@FunctionalInterface` for job processing |\n| `Middleware` | `@FunctionalInterface` for execution middleware |\n| `OJSError` | Sealed interface hierarchy for structured errors |\n| `Workflow` | Chain, group, and batch workflow builders |\n\n### Client Operations\n\n| Method | Description |\n|--------|-------------|\n| `enqueue(type, args)` | Enqueue a job immediately |\n| `enqueue(type, args).queue().send()` | Enqueue with options |\n| `enqueueBatch(requests)` | Atomic batch enqueue |\n| `getJob(id)` | Get job by ID |\n| `cancelJob(id)` | Cancel a job |\n| `health()` | Server health check |\n| `createWorkflow(definition)` | Create a workflow |\n| `getWorkflow(id)` | Get workflow status |\n\n### Worker Configuration\n\n| Option | Default | Description |\n|--------|---------|-------------|\n| `url` | required | OJS server URL |\n| `queues` | `[\"default\"]` | Queues to process (priority order) |\n| `concurrency` | `10` | Max concurrent jobs |\n| `gracePeriod` | `25s` | Shutdown grace period |\n| `heartbeatInterval` | `5s` | Heartbeat frequency |\n| `pollInterval` | `1s` | Polling frequency when idle |\n\n## JSON Serialization\n\nThe SDK includes a **built-in JSON parser** (zero dependencies) that handles all OJS types out of the box. No additional libraries are required.\n\n### Built-in Parser (Default)\n\nThe built-in parser uses `java.net.http.HttpClient` and supports all OJS types (Job, RetryPolicy, UniquePolicy, Workflow, etc.) with safety limits (max depth 128, max input 10 MB).\n\n```java\n// Works out of the box — no Jackson needed\nvar client = OJSClient.builder()\n    .url(\"http://localhost:8080\")\n    .build();\n\nvar job = client.enqueue(\"email.send\", Map.of(\"to\", \"user@example.com\"));\n```\n\n### Optional Jackson Support\n\nIf your project already uses Jackson, the SDK types include Jackson annotations (`@JsonProperty`, `@JsonCreator`) for seamless integration with your existing serialization pipeline. To use Jackson, add it as a dependency alongside the SDK:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.fasterxml.jackson.core\u003c/groupId\u003e\n    \u003cartifactId\u003ejackson-databind\u003c/artifactId\u003e\n    \u003cversion\u003e2.17.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n**When to use which:**\n- **Built-in parser** (default): Lightweight projects, microservices, or when minimizing dependencies.\n- **Jackson**: When your project already uses Jackson, or when you need custom serialization (e.g., date formats, naming strategies, mixins).\n\n## Real-Time Subscriptions\n\nSubscribe to job state changes via Server-Sent Events (SSE):\n\n```java\n// Subscribe to all events\nclient.subscribe(event -\u003e {\n    System.out.printf(\"Job %s: %s → %s%n\",\n        event.jobId(), event.from(), event.to());\n});\n\n// Subscribe to a specific job\nclient.subscribeJob(jobId, event -\u003e { /* ... */ });\n\n// Subscribe to a queue\nclient.subscribeQueue(\"emails\", event -\u003e { /* ... */ });\n```\n\n## Building\n\n```bash\n# Maven\nmvn clean package\n\n# Gradle\ngradle build\n```\n\n## License\n\nApache 2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenjobspec%2Fojs-java-sdk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenjobspec%2Fojs-java-sdk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenjobspec%2Fojs-java-sdk/lists"}