Skip to content

한국어 | English

Reference index

02. Messaging

This category covers message ownership, the receive envelope types (Received, TopicMessage, SubscriptionEvent), and the shared send/request/reply operation-builder family every socket type's entry point returns. The exact signatures are owned by contracts/messaging/.


Message

Owns one native zlink frame — the unit every send, request, reply, and receive API moves. Java does not expose borrowed-payload wrappers, because native queue lifetime is not safely bounded by Java object reachability — every from(...) factory copies into message-owned storage.

Message empty = new Message();
Message sized = new Message(4096);
Message copy = Message.from("payload".getBytes(StandardCharsets.UTF_8));
Message fromString = Message.from("hello");

Options.

Member Meaning
Message() empty
Message(int size) writable storage
allocate(int) static factory, same as Message(int)
from(byte[]) / from(byte[] data, int offset, int length) copies the full array, or the selected range
from(Message) copies another message's payload
from(String) UTF-8 encode
from(ByteBuffer) copies remaining bytes without mutating the source cursor
from(io.netty.buffer.ByteBuf) Netty interop, copies readable bytes without mutating the source cursor
size() payload length in bytes
more() multipart-continuation flag
refCount() native reference count, diagnostic only
empty()/isEmpty() whether size() is zero
data()/toByteArray() copy of the payload as byte[]
toUtf8String() payload decoded as UTF-8
dataBuffer() read-only ByteBuffer view backed by this instance's storage
mutableDataBuffer() writable ByteBuffer view backed by this instance's storage
copyTo(byte[]) / copyTo(byte[], int offset) / copyTo(byte[] dst, int srcOffset, int dstOffset, int length) copies the payload (or a range) into a caller-provided array
copyTo(ByteBuffer) / copyTo(ByteBuf) copies the payload into a caller-provided buffer
tryCopyTo(ByteBuffer) / tryCopyTo(ByteBuf) bounds-checked variant, returns boolean instead of throwing
copyFrom(byte[]|Message, int srcOffset, int dstOffset, int length) copies bytes into this message's storage from a source
readByte/readIntLe/readIntBe/readLongLe/writeByte/writeIntLe/writeIntBe/writeLongLe in-place binary accessors for parsing/writing a wire format directly against message storage
fill(byte) / fill(byte, offset, length) overwrites the payload (or a range) with a repeated byte
contentEquals(byte[]) payload equality check
closeAll(Message[]) / closeAll(Iterable<? extends Message>) static; closes every part in one call, silently ignoring individual close failures

Completion result. Every member is synchronous. Message implements AutoCloseable; sending a message transfers its native frame to the socket, invalidating the instance for further reads — close() releases a message that will not be sent. Out-of-range offsets/lengths throw IndexOutOfBoundsException.

When to use. A sized constructor or a copying from(...) factory to build an outbound payload. The in-place binary accessors to parse/write a wire format directly against message storage without an intermediate byte[]. closeAll(...) to release every part of a received or constructed multipart array in one call instead of a hand-written loop.


Received

Aggregates one recv result: an optional routing id, opaque reply token, and the owned message parts. The returned parts() view is immutable and does not copy the underlying array.

Received received = new Received();
if (router.recv(received)) {
    received.replyToken().ifPresent(token ->
        received.reply().message(Message.from("ok")).submit());
}

Options. Public no-arg constructor Received() for caller-provided storage — the binding overwrites internal state in place on each successful receive, avoiding a per-recv allocation.

Member Returns Meaning
getRoutingId() Optional<RoutingId> present when the receive path provides one
replyToken() Optional<ReplyToken> opaque capability present on a ROUTER request
parts() List<Message>, immutable view every message part this envelope holds
isSinglePart() boolean whether parts() has exactly one element
firstPart() Message the first part, without transferring ownership
singlePartOrThrow() Message the single part, throws if parts() doesn't have exactly one
reply() builder starts the shared ReplyOperation; throws ZlinkSubmitException on submit() if there is no valid reply context
send() builder starts the shared SendOperation, addressed to this envelope's captured source route

Received implements AutoCloseable; close() closes every owned part.

Completion result. All synchronous. firstPart()/singlePartOrThrow() throw ZlinkRecvException when there is no data or the part count doesn't match, respectively — mirroring the receive-side result codes in the Errors category.

When to use. Reuse one Received across a receive loop. Check replyToken() before calling reply() to confirm the envelope is actually replyable.


TopicMessage

Topic-aware recv result used by raw subscription paths: a received publish's topic, source routing id, and message parts.

TopicMessage published = new TopicMessage();
if (sub.subscribe(published)) {
    String topic = published.topic();
}

Options. Public no-arg constructor TopicMessage().

Member Returns Meaning
getRoutingId() Optional<RoutingId> the publisher's routing id, present when the receive path provides one
topic() String the topic this publish was sent on
parts() List<Message> every message part this publish holds
isSinglePart() / firstPart() / singlePartOrThrow() same shape as Received

TopicMessage implements AutoCloseable.

Completion result. Synchronous; firstPart()/singlePartOrThrow() throw ZlinkRecvException the same way as Received's equivalents.

When to use. Reuse one instance across a subscribe-receive loop the same way as Received.


SubscriptionEvent / SubscriptionEntry

Reports one subscriber's subscribe or unsubscribe (as observed by an XPUB socket), and describes one active subscription entry.

SubscriptionEvent evt = new SubscriptionEvent();
if (xpub.receiveSubscriptionEvent(evt)) { /* ... */ }

Options. SubscriptionEvent() public no-arg constructor.

Type Member Meaning
SubscriptionEvent getRoutingId() (Optional<RoutingId>) the subscriber's routing id, present when the receive path provides one
topic() (String) the topic that was subscribed or unsubscribed
subscribed() (boolean) true for a subscribe, false for an unsubscribe
SubscriptionEntry(String filter, boolean pattern) record one active subscription
filterBytes() filter UTF-8 encoded
fromBytes(byte[], boolean) static factory decoding filter from UTF-8 bytes

Completion result. Both are plain data holders with no async behavior; SubscriptionEvent has no close() — it owns no native resources.

When to use. On an XPUB socket's subscription-event receive path (Sockets category) to observe subscriber churn. SubscriptionEntry is the return type of a socket's subscription-snapshot lookup (Sockets category).


Send / request / reply operation-builder shape

The fluent builder every socket type's send/publish/request/reply entry point (Sockets category) returns to accumulate parts, flags, and a terminal submit. All builder interfaces extend the shared MessageBuilderStage<TSubmit> (TSubmit message(Message part)), and the request family additionally extends TimeoutSubmitOperation<List<Message>>.

dealer.send().message(part1).message(part2).submit();

CompletionStage<List<Message>> future = dealer.request()
    .message(Message.from("payload"))
    .timeout(Duration.ofSeconds(5))
    .submit();
List<Message> reply = future.toCompletableFuture().join();

// or on a thread that may block:
List<Message> reply2 = dealer.request().message(Message.from("payload")).submit_sync();

received.reply().message(Message.from("ok")).submit();

Options.

Stage Member Meaning
SendOperation .message(Message) starts the chain
SendSubmitOperation .message(...) / .submit_sync() / .submit() add parts, then choose blocking or CompletionStage terminal
RequestOperation/RequestSubmitOperation same as Send + .timeout(Duration) mirrors the send chain, adding a reply-wait timeout
RequestSubmitOperation terminals .submit_sync() / .submit() blocking reply result or completion-backed CompletionStage reply
ReplyOperation/ReplySubmitOperation mirrors Send no flags stage — the underlying reply function takes no send-flag argument
TimeoutSubmitOperation .timeout(Duration) sets only the request reply timeout; it does not add a send deadline

Completion result.

Terminal Returns Meaning
SendSubmitOperation.submit_sync() void blocks for Core local admission; failures throw ZlinkException
SendSubmitOperation.submit() CompletionStage<Void> DONTWAIT submit settled from the socket completion queue
ReplySubmitOperation.submit() void throws ZlinkException on failure
RequestSubmitOperation.submit() CompletionStage<List<Message>> the caller owns and must close the reply messages
RequestSubmitOperation.submit_sync() List<Message> blocks until the completion queue yields the reply; caller closes parts

Every builder consumes its accumulated public Message parts on a successful submit only. On failure, binding-owned native staging preserves the public parts for the caller even though Core consumes each staging part actually passed to a synchronous call.

When to use. submit()'s CompletionStage in ordinary async code and submit_sync() on a thread that may block or park. Received.reply()/send() rather than reconstructing the destination route by hand.


See contracts/messaging/ and the Java binding spec for the full rationale.