Apache ActiveMQ Artemis transport
com.babelqueue:babelqueue-artemis is an Apache ActiveMQ Artemis transport on the Java core,
built on JMS (Jakarta Messaging 3.x). It sends the
canonical envelope as the message body with the
§7 projection, and consumes by routing
each message to a handler by URN — so a message it produces is consumed by any other BabelQueue
SDK, and vice-versa. Artemis speaks AMQP 1.0, so the other SDKs interoperate over the wire even
though Java uses JMS.
Install
Maven:
<dependency>
<groupId>com.babelqueue</groupId>
<artifactId>babelqueue-artemis</artifactId>
<version>1.0.0</version>
</dependency>
Requirements: Java 17+. It pulls babelqueue-core transitively; bring your Artemis JMS
client (org.apache.activemq:artemis-jakarta-client), which supplies both the jakarta.jms API
and the broker connection.
Produce
import com.babelqueue.artemis.ArtemisPublisher;
import jakarta.jms.*;
import org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory;
import java.util.Map;
ConnectionFactory factory = new ActiveMQJMSConnectionFactory("tcp://localhost:61616");
try (JMSContext ctx = factory.createContext("user", "pass")) {
Session session = ctx.createSession(Session.CLIENT_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(session.createQueue("orders"));
String id = ArtemisPublisher.create(session, producer)
.publish("urn:babel:orders:created", Map.of("order_id", 1042L));
}
publish(urn, data) returns the message meta.id; overloads add a traceId and a relative
Duration delay (native JMS 2.0 setDeliveryDelay).
Consume
import com.babelqueue.artemis.*;
import jakarta.jms.*;
Session session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
MessageConsumer consumer = session.createConsumer(session.createQueue("orders"));
MessageProducer dlqProducer = session.createProducer(null); // anonymous, for <queue>.dlq
ArtemisConsumer worker = ArtemisConsumer.builder(consumer, session)
.handler("urn:babel:orders:created", (envelope, message) -> {
// envelope.data(), envelope.traceId(), envelope.attempts() ...
})
.deadLetterQueue(dlqProducer, "orders.dlq")
.maxTries(3)
.build();
connection.start();
worker.run(() -> true); // receive → process → acknowledge
A successful handler acknowledges the message. A throwing handler leaves it unacknowledged and
recovers the session, so the broker redelivers it and bumps JMSXDeliveryCount; once maxTries
is reached the envelope goes to <queue>.dlq with a dead_letter block. The consumer routes on
JMSType. Unknown-URN strategy is one of fail / delete / release / dead_letter.
One message per poll() keeps the session-wide acknowledge() / recover() correct — a JMS
session is single-threaded, so run one ArtemisConsumer per thread.
Contract mapping (§7)
| Envelope | Apache ActiveMQ Artemis (JMS) |
|---|---|
| body | message body (TextMessage, byte-identical across SDKs) |
job (URN) |
JMSType (consumer routes on this) |
trace_id |
JMSCorrelationID |
meta.id |
JMSMessageID (broker-set for JMS; body is authoritative) |
meta.schema_version |
property bq_schema_version |
meta.lang |
property bq_source_lang |
meta.created_at |
JMSTimestamp (Unix ms) |
attempts |
max(body, JMSXDeliveryCount − 1) (broker counter is 1-based) |
| reserve / ack | receive → process → acknowledge() (CLIENT_ACKNOWLEDGE) |
| retry / delay | recover() redelivery · native setDeliveryDelay |
| dead-letter | <queue>.dlq + dead_letter block (alongside the native DLA) |
The JMS interfaces are mocked with Mockito — no Artemis, no network. The envelope is unchanged
(schema_version stays 1); Apache ActiveMQ Artemis is purely additive.