Reliability & governance helpers
Beyond produce/consume, the Java core ships optional, zero-dependency helpers that
mechanise the reliability and governance contracts the wire spec defines. None touch the
frozen envelope (schema_version: 1); each is a tooling layer over the codec. They are the
Java face of the cross-SDK spec — follow the linked spec page for the full contract.
Idempotency
com.babelqueue.idempotency.Idempotent.wrap(store, handler) makes a handler run at most
once per meta.id, even on at-least-once redelivery.
import com.babelqueue.idempotency.Idempotent;
import com.babelqueue.idempotency.InMemoryStore;
Store store = new InMemoryStore(); // tests / single process
Handler guarded = Idempotent.wrap(store, handler);
The in-memory InMemoryStore is the reference; for a fleet, implement the Store interface
(seen / remember / forget) over a shared backend (no persistent store ships in core yet
— bring your own). See Idempotency and the
store deep-dive.
Transactional outbox
The com.babelqueue.outbox package removes the producer dual write: persist the encoded
envelope in the same DB transaction as your business row, then a relay publishes the
durable rows verbatim.
import com.babelqueue.outbox.Outbox;
import com.babelqueue.outbox.OutboxRelay;
import com.babelqueue.outbox.InMemoryOutboxStore;
OutboxStore store = new InMemoryOutboxStore(); // production: a JDBC-backed OutboxStore
Outbox outbox = new Outbox(store);
// inside YOUR DB transaction, beside the business write — no commit of its own:
String id = outbox.write(envelope); // encodes via EnvelopeCodec, calls store.save()
// later, a relay drains the durable rows to the broker:
OutboxRelayResult res = new OutboxRelay(transport, store).drain(0); // res.published() / res.failed()
OutboxStore is the four-method interface (save / fetchUnpublished / markPublished /
markFailed); the relay forwards through OutboxTransport.publish(queue, body). The
transaction boundary is yours. See Transactional Outbox.
DLQ redrive & replay-bypass
com.babelqueue.Redrive.redrive(transport, dlq, options) moves dead-lettered messages back
onto a queue — dead_letter removed, attempts reset to 0, everything else preserved —
with toQueue (sandbox), max, dryRun, select and bypass. The Replay guard skips
effects that already fired on a deliberate replay; the replay flag rides a ThreadLocal scope
(Replay.process(headers, …)), so Replay.isReplay() takes no argument:
import com.babelqueue.Replay;
Replay.process(headers, () -> {
saveOrder(data); // idempotent core — always runs
Replay.bypassExternalEffects(() -> sendEmail(data)); // external effect — skipped on replay
});
Redrive with bypass(true) stamps the bq-replay-bypass marker through a HeaderPublisher
transport. See DLQ redrive & replay-bypass.
GDPR field encryption
com.babelqueue.gdpr.Gdpr.protect() / .unprotect() encrypt only the data leaves a schema
marks x-gdpr-sensitive, in place. Cipher is caller-bound; AesGcmCipher (on the JDK’s
javax.crypto) is the reference. Validate cleartext — protect after validation on produce,
unprotect before validation on consume.
import com.babelqueue.gdpr.Gdpr;
import com.babelqueue.gdpr.AesGcmCipher;
Cipher cipher = new AesGcmCipher(key); // or implement com.babelqueue.gdpr.Cipher over a KMS
Gdpr.protect(data, schema, cipher); // producer: encrypt marked leaves
try {
Gdpr.unprotect(data, schema, cipher); // consumer: inverse
} catch (DecryptException e) {
// wrong key / tampered → retry / dead-letter
}
com.babelqueue.schema.SensitivePaths.of(schema) exposes the marked leaves directly. See
GDPR field encryption.
OpenTelemetry (traceparent)
com.babelqueue.otel.Tracing emits publish <urn> / process <urn> spans and, on a
header-carrying transport, injects/extracts the W3C traceparent so a consumer span is a true
child of the producer span — degrading to v0.1 trace_id correlation otherwise.
Tracing.publish(…, HeaderSender) injects on produce and the
Tracing.wrapHandler(tracer, handler, Supplier<Map<String,String>>) overload extracts on
consume. The Spring AMQP, SQS and Redis transports carry the header. opentelemetry-api is the
core’s already-optional dependency, so no new dependency is added. See
Observability.
Per-URN schema validation
The com.babelqueue.schema validator (producer guard + consumer wrap, via MapProvider)
validates a message’s data against the JSON Schema registered for its URN. See
Per-URN schema validation.