Skip to content

Commit 1be478a

Browse files
committed
feat: BabelQueue Java core v0.1.0 (com.babelqueue:babelqueue-core)
0 parents  commit 1be478a

31 files changed

Lines changed: 1839 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
test:
13+
runs-on: ubuntu-latest
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
java: [ "17", "21" ]
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- uses: actions/setup-java@v4
22+
with:
23+
distribution: temurin
24+
java-version: ${{ matrix.java }}
25+
cache: maven
26+
27+
- name: Test
28+
run: mvn -B --no-transfer-progress verify

.github/workflows/release.yml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags: [ "v*" ]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
release:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- uses: actions/setup-java@v4
17+
with:
18+
distribution: temurin
19+
java-version: "17"
20+
cache: maven
21+
# Writes a settings.xml server entry `central` using the env vars below,
22+
# and imports the GPG key for signing.
23+
server-id: central
24+
server-username: CENTRAL_TOKEN_USERNAME
25+
server-password: CENTRAL_TOKEN_PASSWORD
26+
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
27+
gpg-passphrase: MAVEN_GPG_PASSPHRASE
28+
29+
- name: Run tests
30+
run: mvn -B --no-transfer-progress verify
31+
32+
- name: Publish to Maven Central
33+
run: mvn -B --no-transfer-progress -Prelease -DskipTests deploy
34+
env:
35+
CENTRAL_TOKEN_USERNAME: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
36+
CENTRAL_TOKEN_PASSWORD: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
37+
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
38+
39+
- name: Create GitHub Release
40+
uses: softprops/action-gh-release@v2
41+
with:
42+
generate_release_notes: true

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Maven build output
2+
target/
3+
*.class
4+
5+
# OS / editor
6+
.DS_Store
7+
*.log
8+
.idea/
9+
*.iml
10+
.vscode/

CHANGELOG.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Changelog
2+
3+
All notable changes to `com.babelqueue:babelqueue-core` are documented here.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
The envelope wire format is versioned separately by `meta.schema_version`
8+
(currently **1**) — see the contract at [babelqueue.com](https://babelqueue.com).
9+
10+
## [Unreleased]
11+
12+
## [0.1.0] - 2026-06-06
13+
14+
### Added
15+
- `EnvelopeCodec` — builds (`make`, `fromMessage`), encodes and decodes the
16+
canonical `{job, trace_id, data, meta, attempts}` envelope (`schema_version` 1).
17+
The single Java implementation of the wire format.
18+
- `Envelope` / `Meta` / `DeadLetter` immutable `record` types.
19+
- `EnvelopeCodec.encode` emits compact UTF-8 JSON (slashes/unicode unescaped) —
20+
byte-identical to the PHP, Python and Node cores (insertion order preserved).
21+
- `EnvelopeCodec.urn(...)` — resolve the URN (`job`, accepting `urn` as an alias).
22+
- `EnvelopeCodec.accepts(...)` — consumer-side validation (rejects empty URN,
23+
unsupported `meta.schema_version`, missing `data`, blank `trace_id`).
24+
- `DeadLetters.annotate(...)` — additive `dead_letter` block builder.
25+
- Contracts `PolyglotMessage` / `HasTraceId`.
26+
- `UnknownUrnStrategy` (`FAIL` / `DELETE` / `RELEASE` / `DEAD_LETTER`);
27+
`BabelQueueException` / `UnknownUrnException`.
28+
- A built-in minimal JSON reader/writer so the core ships with **zero
29+
dependencies** — no Jackson/Gson forced on consumers.
30+
- Shared cross-SDK **conformance suite** under `src/test/resources/conformance/`
31+
(vendored from the canonical `conformance/` set) plus a runner.
32+
33+
### Notes
34+
- Pre-1.0: the public API may change before the `1.0.0` tag.
35+
- **Zero runtime dependencies** (pure JDK); requires Java **17+**.
36+
37+
[Unreleased]: https://github.com/BabelQueue/babelqueue-java/compare/v0.1.0...HEAD
38+
[0.1.0]: https://github.com/BabelQueue/babelqueue-java/releases/tag/v0.1.0

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 Muhammet Şafak
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# BabelQueue for Java
2+
3+
[![CI](https://github.com/BabelQueue/babelqueue-java/actions/workflows/ci.yml/badge.svg)](https://github.com/BabelQueue/babelqueue-java/actions/workflows/ci.yml)
4+
[![Maven Central](https://img.shields.io/maven-central/v/com.babelqueue/babelqueue-core.svg)](https://central.sonatype.com/artifact/com.babelqueue/babelqueue-core)
5+
[![javadoc](https://javadoc.io/badge2/com.babelqueue/babelqueue-core/javadoc.svg)](https://javadoc.io/doc/com.babelqueue/babelqueue-core)
6+
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
7+
8+
> **Polyglot Queues, Simplified.** Read and write the canonical BabelQueue message
9+
> envelope from Java — so your Java/Spring services exchange messages with Laravel,
10+
> Symfony, Python, Go and Node over one strict JSON format, on the broker you
11+
> already run.
12+
13+
This is the framework-agnostic **Java core**: the wire-envelope codec, contracts
14+
and dead-letter helpers — **zero dependencies** (pure JDK, including its own
15+
minimal JSON codec, so no Jackson/Gson is forced on you). The full standard is
16+
documented at **[babelqueue.com](https://babelqueue.com)**.
17+
18+
## Installation
19+
20+
Maven:
21+
22+
```xml
23+
<dependency>
24+
<groupId>com.babelqueue</groupId>
25+
<artifactId>babelqueue-core</artifactId>
26+
<version>0.1.0</version>
27+
</dependency>
28+
```
29+
30+
Gradle:
31+
32+
```kotlin
33+
implementation("com.babelqueue:babelqueue-core:0.1.0")
34+
```
35+
36+
Requires Java **17+**.
37+
38+
## Usage
39+
40+
```java
41+
import com.babelqueue.*;
42+
import java.util.Map;
43+
44+
// Produce — build the canonical envelope and publish the JSON to your broker.
45+
Envelope env = EnvelopeCodec.make(
46+
"urn:babel:orders:created",
47+
Map.of("order_id", 1042L),
48+
"orders",
49+
null);
50+
String body = EnvelopeCodec.encode(env); // compact UTF-8 JSON
51+
// jedis.rpush("queues:orders", body);
52+
// / channel.basicPublish("", "orders", props, body.getBytes(StandardCharsets.UTF_8));
53+
54+
// Consume — decode a message produced by ANY BabelQueue SDK.
55+
Envelope in = EnvelopeCodec.decode(body);
56+
if (EnvelopeCodec.accepts(in)) {
57+
switch (EnvelopeCodec.urn(in)) {
58+
case "urn:babel:orders:created" ->
59+
System.out.println(in.data().get("order_id") + " " + in.traceId());
60+
default -> { /* unknown URN */ }
61+
}
62+
}
63+
```
64+
65+
The envelope is identical to every other SDK's:
66+
67+
```json
68+
{
69+
"job": "urn:babel:orders:created",
70+
"trace_id": "",
71+
"data": { "order_id": 1042 },
72+
"meta": { "id": "", "queue": "orders", "lang": "java", "schema_version": 1, "created_at": 1749132727000 },
73+
"attempts": 0
74+
}
75+
```
76+
77+
> JSON numbers decode into `data` as `Long` (integers) or `Double` (decimals);
78+
> objects as `LinkedHashMap` (insertion order preserved). `encode` leaves slashes
79+
> and non-ASCII unescaped, so the bytes match the PHP/Python/Node cores.
80+
81+
### Typed messages (optional)
82+
83+
```java
84+
record OrderCreated(long orderId) implements PolyglotMessage, HasTraceId {
85+
public String getBabelUrn() { return "urn:babel:orders:created"; }
86+
public Map<String, Object> toPayload() { return Map.of("order_id", orderId); }
87+
public String getBabelTraceId() { return null; } // or an inbound trace to continue
88+
}
89+
90+
Envelope env = EnvelopeCodec.fromMessage(new OrderCreated(1042L), "orders");
91+
```
92+
93+
### Dead-letter
94+
95+
```java
96+
Envelope dlq = DeadLetters.annotate(env, "failed", "orders", 3, "boom", "java.lang.RuntimeException");
97+
// publish EnvelopeCodec.encode(dlq) to the "orders.dlq" queue
98+
```
99+
100+
`DeadLetters.annotate` returns a copy — the original envelope is preserved
101+
unchanged inside the dead-lettered message, so any-language consumers can still
102+
read it.
103+
104+
## What this core is (and isn't)
105+
106+
It enforces the **contract**: the envelope shape, URN identity, trace propagation,
107+
schema-version gating and the dead-letter block. It is intentionally **not** a
108+
worker/runtime — broker wiring, acks and retry loops stay in your own code (or a
109+
future Spring adapter), exactly as with the other SDK cores.
110+
111+
`UnknownUrnStrategy` (`FAIL`, `DELETE`, `RELEASE`, `DEAD_LETTER`) is provided for
112+
adapters to act on.
113+
114+
## Conformance
115+
116+
This core passes the shared **cross-SDK conformance suite** (vendored under
117+
[`src/test/resources/conformance/`](src/test/resources/conformance)) — the same
118+
fixtures every BabelQueue SDK must satisfy, so a Java producer and, say, a Laravel
119+
consumer agree byte-for-byte.
120+
121+
```bash
122+
mvn test
123+
```
124+
125+
## License
126+
127+
[MIT](LICENSE) © Muhammet Şafak

0 commit comments

Comments
 (0)