Class Itinerary
The second of the two routing slips this library speaks, and the one for when there is no
declaration to resolve against. RoutingSlip writes step
names — validate,enrich,dispatch — which a Pipeline turns into
exchanges and routing keys because it knows what those steps are. That is the better model
when the pipeline is known up front: the wire stays readable in a management console, and a
route is one short header rather than a document.
It is no model at all when the itinerary is decided per message and the consumer has never heard of it. This one carries the exchange and the routing key of every stop, so a service that knows nothing but how to read JSON can send the message onwards:
Itinerary itinerary = Itinerary.empty()
.then("orders-events", "order.validate", "validate")
.then("orders-events", "order.charge", "charge")
.then("orders-events", "order.ship", "ship");
The shape on the wire is the one Go, Python and Ruby write, field for field, because those three agreed on it first and a slip is worth nothing if only one library can read it:
{"steps":[{"exchange":"orders-events","routingKey":"order.charge","name":"charge"}],
"done":[{"exchange":"orders-events","routingKey":"order.validate","name":"validate",
"completedAt":"2026-09-09T14:03:11Z"}]}
done is what has already happened, oldest first, so a slip that fails halfway says
how far it got. It is omitted entirely while it is empty, which is what the Go and Ruby
libraries do and what the Python one reads.
Immutable: then(java.lang.String, java.lang.String) and advance() return new instances. The Go and Ruby
libraries mutate while a slip is being assembled and copy on advance; this one copies at both
ends, because an itinerary held in a field and sent twice must not have been changed by the
first send.
-
Nested Class Summary
Nested Classes -
Field Summary
Fields -
Method Summary
-
Field Details
-
HEADER
The header the itinerary travels in:acemq-routing-slip.- See Also:
-
-
Method Details
-
empty
- Returns:
- an itinerary with no stops on it yet
-
of
- Parameters:
stops- the stops, in order- Returns:
- an itinerary of those stops, with nothing done yet
-
then
Adds a stop to the end.- Parameters:
exchange- where that stop's message goes, empty for the default exchangeroutingKey- the routing key, which is the queue name when publishing without an exchange- Returns:
- a new itinerary with the stop appended
-
then
- Parameters:
exchange- where that stop's message goesroutingKey- the routing keyname- what to call it in a log, which need not be anything- Returns:
- a new itinerary with the stop appended
-
next
- Returns:
- the stop this message is going to, or empty when the itinerary is finished
-
advance
- Returns:
- an itinerary with the first stop moved to
done, stamped with the clock
-
isFinished
public boolean isFinished()- Returns:
- whether every stop has been made
-
steps
- Returns:
- the stops still to make, in order
-
done
- Returns:
- the stops already made, oldest first
-
from
Reads an itinerary off a message's headers.- Parameters:
headers- the headers as the handler was given them- Returns:
- the itinerary, or empty when the message is not carrying one
- Throws:
AceFatalException- when the header is there and cannot be read. Fatal rather than retryable, and deliberately so: a slip that will not parse will not parse on the next attempt either, and a message going round the broker while nothing can tell where it is meant to go is the worst of both outcomes.
-
parse
- Parameters:
json- the itinerary as it travels- Returns:
- the itinerary
- Throws:
AceFatalException- when the text is not an itinerary
-
toHeader
- Returns:
- the itinerary as it goes on the wire, to publish under
HEADER
-
equals
-
hashCode
public int hashCode() -
toString
-