Class Envelope

java.lang.Object
org.acemq.amqp.api.Envelope

public final class Envelope extends Object
The AceMQ metadata that travels with every message, independent of payload and broker.

An envelope is immutable. The engine derives a new one rather than mutating an existing one, which is what makes an attempt counter trustworthy when the same message is delivered several times.

This is a value type. It would be a record, but the published bytecode targets Java 11 so that Spring Boot 2.7 applications can consume the library; see ADR-015.

See Also:
  • Method Details

    • of

      public static Envelope.Builder of(String type)
      Starts a new envelope for the given logical message type.
      Parameters:
      type - logical message type, for example order.placed
      Returns:
      a builder with an identifier and timestamp already generated
    • id

      public String id()
      Returns:
      the unique message identifier, also the default idempotency key
    • type

      public String type()
      Returns:
      the logical message type
    • version

      public int version()
      Returns:
      the payload schema version, at least 1
    • correlationId

      public String correlationId()
      Returns:
      the business correlation identifier, defaulting to id()
    • causationId

      public Optional<String> causationId()
      Returns:
      the identifier of the message that caused this one, if any
    • attempt

      public int attempt()
      Returns:
      the delivery attempt number, starting at 1 for a first delivery
    • firstSeen

      public Instant firstSeen()
      Returns:
      when this message was first published, used for age-based give-up
    • origin

      public Optional<String> origin()
      Returns:
      the publishing process, conventionally service@host, if known
    • error

      public Optional<String> error()
      Why this message was dead-lettered or parked.

      Present only on a message read back from a dead-letter or parking queue. It is an envelope field rather than an ordinary header because AceMQ owns the x-acemq- namespace: were it left as a header it would be filtered out of headers() and become invisible to the very code that needs it most.

      Returns:
      the failure reason recorded when the message was given up on
    • claim

      public Optional<String> claim()
      Where the payload is, when the application stores it outside the message.

      An optional field an application may set to tell an operator reading a dead-letter queue where a payload went — conventionally a URI. AceMQ neither writes nor interprets it; it is carried, like origin(), because the name is reserved and a header in the x-acemq- namespace would otherwise be stripped before a handler could see it.

      Not the claim-check pattern. ClaimCheckCodec frames the reference in the body and sets no header at all, deliberately: a header can be stripped by a shovel or a federation link, and a present-or-absent header cannot say whether a payload travelled inline. That is unchanged and is not what this field is for.

      Java reserved this name and materialised nothing, so a claim set by a Go, .NET, Python or Ruby publisher was dropped on the way in with nothing reporting the loss. All five now read and write it.

      Returns:
      where the payload is, when the publisher said so
    • replayedFrom

      public Optional<String> replayedFrom()
      Where this message was replayed from, when it was.

      Set by mq.replay(...) when a message is moved back out of a dead-letter queue or parking lot. On the wire this is acemq-replayed-from, in the shared namespace rather than the reserved one, so it reaches a handler's headers as well as this field — the one piece of evidence that a message has been round the loop before is of no use to the code handling it if the engine strips it on the way in.

      Returns:
      the queue it came back from, when it is a replay
    • replayedAt

      public Optional<Instant> replayedAt()
      Returns:
      when it was last replayed, when it is a replay
    • replayCount

      public int replayCount()
      Returns:
      how many times this message has been replayed; zero for one that never has. Distinct from attempt(), which a replay resets — a message on its fifth trip through a dead-letter queue would otherwise look brand new
    • route

      public Optional<RoutingSlip> route()
      Where this message is going, when it is travelling a pipeline.

      A field rather than a header, for the same reason the error is one: the AceMQ prefix is closed to application headers, so a slip put there by hand would be silently dropped on the way in. A step reads its route from here.

      Returns:
      the routing slip, or empty when the message is not part of a pipeline
    • headers

      public Map<String,Object> headers()
      Returns:
      application headers, excluding the AceMQ-owned ones; never null
    • age

      public Duration age()
      Returns the age of the message, measured from its first publish.

      Retry policies use this to abandon a message that has been circulating too long, regardless of how many attempts remain.

      Returns:
      elapsed time since firstSeen()
    • nextAttempt

      public Envelope nextAttempt()
      Returns a copy of this envelope representing the next delivery attempt.

      Everything else is preserved, so firstSeen() continues to measure from the original publish rather than from the retry.

      Returns:
      a new envelope with attempt() incremented by one
    • causing

      public Envelope.Builder causing(String type)
      Returns a builder for a message caused by this one.

      The correlation identifier is carried across so a whole flow can be followed, and the causation identifier is set to this message's identifier so the immediate parent is recorded. The attempt counter restarts, because the new message is a first publish.

      Parameters:
      type - logical message type of the new message
      Returns:
      a builder pre-wired for causation tracking
    • toBuilder

      public Envelope.Builder toBuilder()
      Returns:
      a builder initialised with every value from this envelope
    • equals

      public boolean equals(Object other)
      Overrides:
      equals in class Object
    • hashCode

      public int hashCode()
      Overrides:
      hashCode in class Object
    • toString

      public String toString()
      Overrides:
      toString in class Object