Skip to content

Commit 42a50f8

Browse files
committed
feat: BabelQueue Spring Boot adapter v0.1.0 (com.babelqueue:babelqueue-spring)
0 parents  commit 42a50f8

16 files changed

Lines changed: 866 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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
server-id: central
22+
server-username: CENTRAL_TOKEN_USERNAME
23+
server-password: CENTRAL_TOKEN_PASSWORD
24+
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
25+
gpg-passphrase: MAVEN_GPG_PASSPHRASE
26+
27+
- name: Run tests
28+
run: mvn -B --no-transfer-progress verify
29+
30+
- name: Publish to Maven Central
31+
run: mvn -B --no-transfer-progress -Prelease -DskipTests deploy
32+
env:
33+
CENTRAL_TOKEN_USERNAME: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
34+
CENTRAL_TOKEN_PASSWORD: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
35+
MAVEN_GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
36+
37+
- name: Create GitHub Release
38+
uses: softprops/action-gh-release@v2
39+
with:
40+
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: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Changelog
2+
3+
All notable changes to `com.babelqueue:babelqueue-spring` 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+
- `BabelQueueMessageConverter` — a Spring AMQP `MessageConverter` that encodes the
16+
canonical BabelQueue envelope on produce (with the contract AMQP properties:
17+
`type` = URN, `correlationId` = trace_id, `messageId` = meta.id,
18+
`x-schema-version` / `x-source-lang` / `x-attempts`) and decodes it on consume,
19+
returning a core `Envelope`. Rejects non-conformant messages.
20+
- `BabelQueuePublisher` — ergonomic producer over `RabbitTemplate`
21+
(`publish(urn, data, queue)` / `publish(PolyglotMessage)`).
22+
- `BabelQueueAutoConfiguration` — Spring Boot auto-configuration exposing the
23+
converter (so Boot wires it into the `RabbitTemplate` and listener factory) and
24+
the publisher; both back off if the application defines its own.
25+
- `BabelQueueProperties``babelqueue.default-queue`.
26+
27+
### Notes
28+
- Pre-1.0: the public API may change before the `1.0.0` tag.
29+
- Built on the framework-agnostic `com.babelqueue:babelqueue-core`; targets Spring
30+
Boot **3** and Java **17+**.
31+
32+
[Unreleased]: https://github.com/BabelQueue/babelqueue-spring/compare/v0.1.0...HEAD
33+
[0.1.0]: https://github.com/BabelQueue/babelqueue-spring/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: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# BabelQueue for Spring
2+
3+
[![CI](https://github.com/BabelQueue/babelqueue-spring/actions/workflows/ci.yml/badge.svg)](https://github.com/BabelQueue/babelqueue-spring/actions/workflows/ci.yml)
4+
[![Maven Central](https://img.shields.io/maven-central/v/com.babelqueue/babelqueue-spring.svg)](https://central.sonatype.com/artifact/com.babelqueue/babelqueue-spring)
5+
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
6+
7+
> **Polyglot Queues, Simplified.** A Spring Boot adapter that makes your Spring
8+
> services produce and consume the canonical BabelQueue message envelope over
9+
> RabbitMQ — so they exchange messages with Laravel, Symfony, Python, Go, Node and
10+
> .NET over one strict JSON format.
11+
12+
This is the **Spring adapter** on top of the framework-agnostic
13+
[`babelqueue-core`](https://github.com/BabelQueue/babelqueue-java): a Spring AMQP
14+
`MessageConverter`, an ergonomic publisher, and Spring Boot auto-configuration. The
15+
full standard is documented at **[babelqueue.com](https://babelqueue.com)**.
16+
17+
## Installation
18+
19+
Maven:
20+
21+
```xml
22+
<dependency>
23+
<groupId>com.babelqueue</groupId>
24+
<artifactId>babelqueue-spring</artifactId>
25+
<version>0.1.0</version>
26+
</dependency>
27+
```
28+
29+
Gradle:
30+
31+
```kotlin
32+
implementation("com.babelqueue:babelqueue-spring:0.1.0")
33+
```
34+
35+
Bring your own Spring AMQP (the adapter targets **Spring Boot 3**, Java **17+**):
36+
37+
```kotlin
38+
implementation("org.springframework.boot:spring-boot-starter-amqp")
39+
```
40+
41+
## How it works
42+
43+
Auto-configuration registers a single `MessageConverter` bean
44+
(`BabelQueueMessageConverter`). Spring Boot wires it into **both** the
45+
`RabbitTemplate` (producing) and the default `@RabbitListener` container factory
46+
(consuming), so the canonical envelope is the wire format with no extra setup. Both
47+
beans back off if your application defines its own.
48+
49+
## Producing
50+
51+
```java
52+
import com.babelqueue.spring.BabelQueuePublisher;
53+
import java.util.Map;
54+
55+
@Service
56+
class Orders {
57+
private final BabelQueuePublisher babelQueue;
58+
59+
Orders(BabelQueuePublisher babelQueue) {
60+
this.babelQueue = babelQueue;
61+
}
62+
63+
void create() {
64+
babelQueue.publish("urn:babel:orders:created", Map.of("order_id", 1042L), "orders");
65+
}
66+
}
67+
```
68+
69+
Or build a typed message by implementing `com.babelqueue.PolyglotMessage` and
70+
`babelQueue.publish(message, "orders")`. Either way the broker receives:
71+
72+
```json
73+
{
74+
"job": "urn:babel:orders:created",
75+
"trace_id": "",
76+
"data": { "order_id": 1042 },
77+
"meta": { "id": "", "queue": "orders", "lang": "java", "schema_version": 1, "created_at": 1749132727000 },
78+
"attempts": 0
79+
}
80+
```
81+
82+
…plus the contract AMQP properties (`type` = URN, `correlation_id` = trace_id,
83+
`message_id` = meta.id, `x-schema-version` / `x-source-lang` / `x-attempts`).
84+
85+
## Consuming
86+
87+
A `@RabbitListener` method receives the decoded `Envelope` — from any producer, in
88+
any language:
89+
90+
```java
91+
import com.babelqueue.Envelope;
92+
import com.babelqueue.EnvelopeCodec;
93+
94+
@Component
95+
class OrderListener {
96+
@RabbitListener(queues = "orders")
97+
void onMessage(Envelope envelope) {
98+
switch (EnvelopeCodec.urn(envelope)) {
99+
case "urn:babel:orders:created" ->
100+
log.info("[{}] order {}", envelope.traceId(), envelope.data().get("order_id"));
101+
default -> { /* ignore */ }
102+
}
103+
}
104+
}
105+
```
106+
107+
Non-conformant messages (missing URN, unsupported `meta.schema_version`, blank
108+
`trace_id`, missing `data`) raise a `MessageConversionException`, so Spring rejects
109+
them — route them to a dead-letter exchange the usual Spring AMQP way.
110+
111+
## Configuration
112+
113+
```yaml
114+
babelqueue:
115+
default-queue: orders # used by the publisher when no queue is given
116+
```
117+
118+
## License
119+
120+
[MIT](LICENSE) © Muhammet Şafak

0 commit comments

Comments
 (0)