STOMP Usage in MaXaM
Why STOMP?
STOMP is used to allow easier initial connect/interoperation than would be achieved using a pure Avro and MAX defined messaging system. However, it should be recognised that STOMP is an access protocol - there are no end-to-end STOMP systems. Most STOMP access/interfaces are to AMQP which is itself an interface/interop standard for MQ systems. The actual "plumbing" of MaXaM is Avro messaging over internal message delivery and routing mechanisms.
The interface is specified in terms of STOMP. Other/internal MaXaM interfaces exist but are not described in this specification. Regardless of the interface used to send or receive messages, the resulting MaXaM Avro messaging is AS IF this send/receive were performed via STOMP.
STOMP Versions Implemented
STOMP 1.1 and 1.2
STOMP Client Frame Support
Supported frames:
| Command | Header | Status | Notes |
|---|---|---|---|
| standard | content-length | Optional | Avro content implies embedded NULLs so all avro messages MUST use content-length |
| standard | receipt | Optional | |
| CONNECT | accept-version | Required | |
| CONNECT | host | Required | |
| CONNECT | client-id | Optional/Extension | Used to identify a client session (as opposed to a user) in particular for durable subscriptions. Only one connection is allowed for a given client-id. A client wishing to resume durable subscriptions MUST use this header to re-associate the client with the session holding the durable subscriptions to be resumed. Attempts to make multiple connections with the same client-id may result in either the old or new connection being closed with an ERROR. |
| CONNECT | login | Not implemented | Client SSL certificate fingerprints are used as client user identitifiers |
| CONNECT | passcode | Not implemented | |
| CONNECT | heart-beat | Optional | Usage is STRONGLY recommended |
| STOMP | All headers | See CONNECT | STOMP 1.2 Synonym for CONNECT - see CONNECT for supported headers |
| SUBSCRIBE | destination | required | |
| SUBSCRIBE | id | required | |
| SUBSCRIBE | ack | optional | Only "auto" and "client" are supported in this version. "client-individual" is accepted but treated as "client" |
| SUBSCRIBE | durable | Optional/Extension | Set to true for durable subscriptions |
| SUBSCRIBE | eager | Optional/Extension | Set to true to trigger immediate delivery of current state(s) |
| UNSUBSCRIBE | id | required | |
| UNSUBSCRIBE | durable | Optional/Extension | Set to false to remove durable subscriptions |
| DISCONNECT | |||
| SEND | destination | Required | |
| SEND | transaction | Not implemented | Will generate error |
| ACK | subscription | required in v1.1 | Must be sent by the client for each message received if the subscription ack header is set to "client" or "client-individual", and must be subscription from the MESSAGE frame received from the server |
| ACK | message-id | required in v1.1 | Must be sent by the client for each message received if the subscription ack header is set to "client" or "client-individual", and must be message-id from the MESSAGE frame received from the server |
| ACK | id | required in v1.2 | Must be sent by the client for each message received if the subscription ack header is set to "client" or "client-individual", and must be subscription and message-id concatenated joined on space, from the MESSAGE frame received from the server |
Note: ack's set to "client" are intended to ensure that a potentially slow client does not receive obsolete data that simply delays delivery of more current data. If a message is left un-ack'ed indefinitely it will delay or prevent delivery of messages for other topics as well as the unacknowledged topic (whether or not subscriptions to those topics specify "client" acks). To achieve the intended behaviour of ensuring a client has the most up-to date data possible, a client should request client acks on all subscriptions on a connection and must acknowledge all messages in a timely manner.
Unsupported Frames:
| Command | Status | Notes |
|---|---|---|
| NACK | Not implemented | Will be ignored |
| BEGIN | Not implemented | Will generate error |
| COMMIT | Not implemented | Will generate error |
| ABORT | Not implemented | Will generate error |
STOMP Server Frame Support
| Command | Header | Status | Notes |
|---|---|---|---|
| standard | content-length | Optional | Avro content implies embedded NULLs so all avro messages WILL use content-length |
| CONNECTED | version | Required | |
| CONNECTED | session | Populated with a the session identifier that may be used by a client in a CONNECT client-id header to resume a session. | |
| CONNECTED | server | Not implemented | |
| CONNECTED | heart-beat | Not implemented | |
| MESSAGE | destination | required | |
| MESSAGE | message-id | required | |
| MESSAGE | subscription | required | |
| MESSAGE | ack | Will be populated where client sent a SUBSCRIBE frame with "ack" header equal to "client" or "client-individual" | |
| ERROR | message | Optional | Will usually be populated |
| RECEIPT | receipt-id | Required |
Requests from STOMP Clients
All requests except for CONNECT can and SHOULD include a receipt header, so as to request a RECEIPT frame when the request has been processed by the STOMP broker. The value given in the receipt header will be included as receipt-id in the RECEIPT response. The client SHOULD use a sufficiently unique value in each request to ensure that the request being acknowledged by the broker in a RECEIPT frame is able to be uniquely identified.
STOMP CONNECT
Once the TCP connection is established, it is necessary to perform a CONNECT at the STOMP protocol level, to the STOMP broker. The client MUST include the heart-beat header with both send and receive heart-beat intervals set to 10000 (ms) or less. The broker WILL always specify send and receive heart-beat intervals of not less than 10000 (ms) in its CONNECTED response.
The client SHOULD include the host header and must set it to MAXVAPI.
The client MAY include a a login header but this will be ignored. Client identity is established using TLS with client certificates.
A client that wishes to resume durable subscriptions MUST include a client-id header. The id is used to identify a previous client session (NOT a user/client instance). As such only one session (connection) is allowed for a given client-id. Use of this header allows the client to re-associate with the (previous) MaXaM session holding the durable subscriptions.
The response to a CONNECT (or STOMP, which is a synonym for CONNECT) is CONNECTED, unless the connection was rejected, in which case the response will be an ERROR followed by the TCP connection being closed.
STOMP DISCONNECT
To gracefully close a STOMP session, with no loss of data, the client SHOULD send a DISCONNECT with a receipt header and await a RECEIPT for the DISCONNECT, confirming that all previous requests have been processed and all messages in flight to the client have been delivered.
The client MUST NOT send any requests after sending the DISCONNECT.
The client MUST be prepared to process any frames it receives prior to the RECEIPT.
The client MUST handle the case where the broker closes (resets) the transport connection before the client can receive a RECEIPT. To minimise such occurrences and any data loss, the client should attempt to accept/receive post-DISCONNECT data as fast as possible.
STOMP SUBSCRIBE
The client once connected can subscribe to messages using the STOMP SUBSCRIBE frame.
The destination header is used to identify the topics being subscribed to. A glob pattern may be used to subscribe to a range of destinations matching the pattern.
Messages that have been sent (using SEND) to a matching destination will be delivered to the subscribing client subject to filtering based on client permissions. Individual SENDs may not all be delivered if the subscribing client is unable to receive/acknowledge messages at the rate they are being sent to that destination but will ultimately receive the most recent upate for that destination.
The client MUST include an id header in each SUBSCRIBE. A client making a subscription request MUST use a unique subscription identifier per subscription made over a given connection (session). The client MUST retain the subscription id for use in any subsequent UNSUBSCRIBE message. The client MAY use the subscription id to determine which messages received in MESSAGE frames are for that subscription.
Subscriptions may be durable or transient. A durable subscription results in messages for that topic being stored even if the connection/session ends, until the subscribing client re-connects to the session and resumes the subscription. A durable subscription is requested by including a durable:true header in the subscription request.
Note that for a last value queue durability simply means that the latest values for topics that have changed will be delivered, not that any/every intermediate values will be.
A subscribing client with a durable subscription SHOULD re-subscribe, using the same subscription id, after reconnecting using the session id of it's previous session/connection as the value of the client-id CONNECT header). The client should not assume the subscription has actually persisted (e.g., there may have been a system failure). A subscribing client with a durable subscription MUST NOT re-subscribe with a new subscription id unless the client is prepared to receive duplicate messages (delivered to both the old and new subscriptions).
Clients SHOULD include an ack header in their subscription. It is not recommended to set the ack mode to auto. The use of Acks prevents potentially large volumes of stale "backlogged" data in the TCP stack buffers to the client, so improving the ability of the client to remain up to date. This is especially important for subscribers that subscribe to a large number of live topics (eg to get all latest state of all EGMs).
Extension - Durability:
A client MAY use the durable header as described above, if it wishes to resume its subscription as if it had not disconnected, after a disconnection or temporary UNSUBSCRIBE. Note that to permanently UNSUBSCRIBE from a durable subscription the durable:true header must be included in the UNSUBSCRIBE request.
Extension - Eager Subscriptions:
A client SHOULD use the eager header if it wishes to receive the latest (last) value of every last value queue topic in a matching the subscription when it SUBSCRIBEs. Typically this option is simpler to use than using durable as it does not rely on durable shared state (outliving a connection) between MaXaM and the client. The disadvantage, if the subscription is broad, is that a client that disconnects then reconnects will re-receive a large volume of unchanged values.
STOMP UNSUBSCRIBE
The client MUST include a subscription id field from a previous SUBSCRIBE when unsubscribing.
To stop queuing messages for a durable subscription the client MUST include a durable:true header in the unsubscribe request. If this header is not included (or is not true) the UNSUBSCRIBE will stop messages being delivered, but new messages will continue to be queued pending a later re-SUBSCRIBE.
STOMP SEND
The client once connected can send (PUBLISH) messages using the STOMP SEND frame, subject to client role/permissions.
The destination header is used to identify the resource (or topic) being published, not the entity that will consume it. It is up to consumers that wish to receive publications on that topic/destination to subscribe to such messages.
Extension:
A client can DELETE a value published to a last value queue by publishing "nothing" with a STOMP SEND message (content-length 0) to the destination (topic) to be deleted.
STOMP ACK
The client should use acknowledgements as discussed in the SUBSCRIBE request usage, above.
Responses and Notifications to STOMP Clients
STOMP MESSAGE
A client will receive messages (based on its subscriptions and permissions/role) in STOMP MESSAGE frames.
The received messages carry unique message_id headers which should be used wherever an identifier for a specific message is required.
STOMP RECEIPT
Any command that included a receipt header will receive a RECEIPT response from the server.
The RECEIPT may contain a result header. The result header provides additional information on how the request was processed. In particular, it is used to report cases where a request has been intentionally ignored by the server (this is as distinct from an error).
The server may silently ignore requests, with no RECEIPT generated, if the client does not have permission to perform the requested action, access the requested resource, the resource does not exist etc.
Extension:
RECEIPTS may contain a content-disposition header. This is used for Handling Rejected Messages as described below.
STOMP ERROR
A STOMP ERROR is only sent in cases where there is a sufficiently severe issue that the connection will be terminated. Issues that are a simple status/result reporting that a request was unsuccessful are sent in RECEIPT headers as described above.
Handling Rejected Messages
Receipt of a SEND that is rejected by access control or schema validation is not an ERROR (in STOMP terms). In particular, such messages SHALL NOT cause the STOMP connection to be terminated, or prevent or otherwise influence processing of other SEND frames from the same (or any) client. A SEND that is rejected is considered processed by STOMP - as such if it included a receipt header, a RECEIPT shall be generated. The RECEIPT contains, in addition to the receipt id header, a disposition header. The disposition header shall report rejected-invalid, rejected-unauthorised if a message is rejected.
Messages that are accepted normally shall simply report accepted in the disposition header.
The client must assume that any rejected message has had no effect and in particular MUST assume that no client has or will receive it.