Fact-checked by Grok 2 weeks ago

Streaming Text Oriented Messaging Protocol

The Streaming Text Oriented Messaging Protocol (STOMP), also known as the Simple Text Oriented Messaging Protocol, is a lightweight, text-based designed for asynchronous between clients and in distributed systems. It provides an interoperable wire format that enables STOMP-compliant clients to interact with any supporting , facilitating cross-language and cross-platform messaging without proprietary dependencies. Originally developed to bridge enterprise message brokers with scripting languages such as , , and , STOMP has evolved into a mature standard for (MOM), serving as a simpler alternative to more complex protocols like AMQP. STOMP operates over reliable, bidirectional streaming transports such as or , using a frame-based inspired by HTTP requests. Each frame consists of a command (e.g., CONNECT, SEND, or SUBSCRIBE), followed by optional key-value headers, an optional body, and termination by a octet, making it human-readable and easy to implement—even with basic tools like for client testing. Key features include support for message acknowledgments (auto, client, or client-individual modes), transactions, heart-beating to detect connection issues, and content-type negotiation, all while maintaining across versions. The protocol defines opaque destination strings for routing, allowing server-specific semantics without mandating a particular addressing scheme. The protocol's versioning began with STOMP 1.0, followed by 1.1 (introducing features like content-length headers and improved error handling), and the current 1.2 specification released in 2012, which refined aspects like frame line endings and message acknowledgments for enhanced reliability. STOMP 1.2 remains the de facto standard, with widespread implementations in frameworks like , Apache ActiveMQ Artemis, and Eclipse Vert.x, supporting real-time applications such as chat systems, financial trading, and messaging. Its simplicity and interoperability have made it a popular choice for scalable, event-driven architectures, with ongoing community maintenance via the STOMP specification .

Introduction

Definition and Purpose

The Streaming Text Oriented Messaging Protocol (STOMP), also known as the Simple Text Oriented Messaging Protocol, is a lightweight, interoperable that defines a text-based wire format for asynchronous between clients and mediating servers, such as message brokers. This format enables STOMP clients to communicate effectively with enterprise messaging systems in a standardized way, supporting core operations without relying on complex binary encodings. The primary purpose of STOMP is to facilitate asynchronous messaging patterns, including publish-subscribe and point-to-point communication, allowing clients written in various languages to interact with (MOM) systems. It was originally designed to bridge the gap for scripting languages like , , and , which often lacked straightforward interfaces to robust enterprise message brokers, thereby promoting easier integration across diverse MOM implementations. Key design principles of STOMP emphasize simplicity through its human-readable, encoded text format, which is easy to implement and debug across different programming environments. The protocol is transport-agnostic, operating over any reliable bidirectional streaming channel such as or WebSockets, ensuring flexibility without mandating specific lower-layer dependencies. Interoperability is a core philosophy, enabling STOMP to serve as a common denominator for messaging without the overhead of proprietary or protocols. As an application-layer protocol (Layer 7) in the , STOMP functions within to handle high-level messaging semantics, distinct from underlying transport mechanisms.

Historical Development

The Streaming Text Oriented Messaging Protocol (STOMP) emerged in the mid-2000s, developed initially at Codehaus around 2005, to address the challenges faced by scripting languages, such as , , and , in connecting to enterprise message brokers. Initially inspired by and formerly known as the Text Oriented Messaging Protocol (TTMP), it was developed as a lightweight, text-based alternative for asynchronous messaging in environments. An informal specification for STOMP version 1.0 appeared in the mid-2000s, establishing core frame structures for basic operations like sending and subscribing to messages over TCP sockets. Version 1.1 followed as a more formalized update in 2011, introducing key features such as optional heart-beating to detect connection failures and content-length headers to accurately delineate message bodies, particularly when null octets were present. These additions improved reliability and compatibility across diverse implementations. On October 22, 2012, version 1.2 was released, refining server-initiated frames including for conveying protocol violations and enhanced handling for transaction confirmations, while clarifying heart-beating mechanics and line-ending requirements. STOMP has no single corporate owner or formal standards body and is maintained collaboratively by the open-source community. The protocol specifications are hosted on at stomp.github.io, with evolution driven by discussions on the [email protected] mailing list and contributions from developers supporting various brokers and clients. The protocol's adoption accelerated with the standardization of WebSockets in 2011 (RFC 6455), enabling STOMP-over-WebSocket mappings that facilitated real-time bidirectional messaging in browser-based web applications without proprietary extensions. As of 2025, STOMP continues to find use in IoT deployments and real-time systems for its interoperability and low overhead, even alongside newer protocols like and AMQP.

Technical Specification

Frame Structure

The fundamental building block of the Streaming Text Oriented Messaging Protocol (STOMP) is the , which encapsulates commands, headers, and optional bodies in a format. A STOMP frame begins with a command line, consisting of the command name followed by an end-of-line (EOL) marker, which is optionally a (CR, octet 13) followed by a line feed (LF, octet 10), or simply LF. This is followed by zero or more header lines, each in the format key:value terminated by EOL, a blank line (EOL without content), an optional body, and finally a null octet (0x00) terminator. The overall can be represented as:
COMMAND
header1:value1
header2:value2

body content (optional)
\x00
Headers in STOMP frames are encoded strings with case-sensitive keys and values, where keys and values are separated by a colon without spaces around it. Special characters in header values require escaping using C-style backslash sequences: \n for LF, \r for , \c for colon, and \\ for itself; any other backslash is treated as literal and may result in a fatal error if undefined. Common headers include destination for routing, content-type to specify the type (defaulting to text/plain if omitted), and content-length indicating the body's octet count in decimal. If multiple headers share the same key, only the first value is considered valid. The body of a frame, when present, follows the blank line and precedes the null terminator, allowing for text or binary content up to server-imposed limits. Bodies are permitted only in SEND, MESSAGE, and ERROR frames, and their content is UTF-8 encoded by default unless specified otherwise via the content-type header, which supports binary payloads without additional framing restrictions beyond the null terminator. No escaping is applied to the body itself, but it must not contain the null octet, as this would prematurely terminate the frame; servers may reject oversized bodies by sending an ERROR frame and closing the connection. Framing rules emphasize STOMP's text-oriented nature while accommodating structured data transmission over or other octet streams. All commands and headers must use ASCII-compatible characters in , ensuring interoperability, with the null octet serving as the unambiguous delimiter even after optional trailing EOLs. This design avoids binary framing complexities, relying instead on text for commands and headers, though binary bodies are supported to enable versatile messaging.

Commands and Responses

The Streaming Text Oriented Messaging Protocol (STOMP) defines a set of client commands and responses as text-based to facilitate messaging operations between clients and brokers. These commands and responses build upon the protocol's frame structure, where each frame includes a command line, optional headers, an optional body, and a null octet terminator. Client commands initiate actions such as connecting to a , sending , managing subscriptions, handling , and acknowledging receipts. The CONNECT command establishes a connection, requiring the accept-version header to specify supported protocol versions (e.g., 1.2) and the host header for the target ; optional headers include login and passcode for , as well as heart-beat for negotiation. The SEND command publishes a to a destination, mandating the destination header and allowing an optional body for the message content, with the transaction header to group it within a if needed. The SUBSCRIBE command registers interest in from a destination, requiring destination and id headers for unique identification, and an optional ack header to set mode (auto, client, or client-individual, defaulting to auto). Conversely, the UNSUBSCRIBE command removes a subscription using the required id header. The DISCONNECT command requests a graceful , optionally including a receipt header for confirmation. Transaction-related commands manage operations across multiple frames. The BEGIN command starts a with a required transaction header providing a unique identifier. The COMMIT command finalizes the using the same transaction identifier, applying all grouped operations. The ABORT command rolls back the , discarding any pending operations tied to the identifier. Acknowledgment commands handle delivery confirmation in explicit modes. The ACK command confirms receipt of a via its required id header, optionally within a transaction. The NACK command rejects or requeues a using the same id and optional transaction parameters. Server responses acknowledge client actions or deliver content. The CONNECTED frame confirms a successful connection, requiring the version header to indicate the negotiated version and optionally including session for a unique , server for implementation details, and heart-beat for timing. The MESSAGE frame delivers a to a subscriber, mandating destination, message-id, and subscription headers, with an optional ack header in client acknowledgment modes and a body for content. The RECEIPT frame provides confirmation for operations requesting it, requiring the receipt-id header matching the client's request. The ERROR frame signals failures, optionally with a message header for a brief description and a detailed body; it closes the connection upon receipt. Key headers unique to these operations include those for acknowledgments, receipts, and transactions. The ack header specifies modes in SUBSCRIBE (auto for automatic acknowledgment, client for batch, client-individual for per-message) and appears in MESSAGE to indicate required explicit acknowledgments. The receipt header, used in most client commands except CONNECT, requests a RECEIPT response with a unique receipt-id. The transaction header groups compatible commands like SEND, ACK, and NACK under a BEGIN block for atomicity. These elements ensure reliable, interoperable messaging in STOMP version 1.2.

Connection and Messaging Operations

Establishing Connections and Sessions

The process of establishing a connection in the Streaming Text Oriented Messaging Protocol (STOMP) begins with the client sending a CONNECT frame to the server over an underlying transport such as or . This frame includes mandatory headers like accept-version, which specifies the highest STOMP version the client supports (e.g., "1.2"), and host, identifying the target server. Optional headers may include login and passcode for , as well as heart-beat to negotiate intervals. Upon receipt, the server evaluates the request: if the versions are compatible, it responds with a CONNECTED frame containing the selected version, a unique server-assigned session identifier, and optional details like the server implementation name. Incompatible versions prompt an frame from the server, followed by connection closure. Once connected, a STOMP session is established and persists until the client explicitly sends a DISCONNECT frame or the transport connection terminates. The session identifier, provided in the CONNECTED frame's session header, uniquely identifies the session on the and enables stateful interactions, such as transaction management. Servers assign sessions dynamically, ensuring persistence across operations until disconnection, which helps maintain context for ongoing messaging activities. Heart-beating, introduced in STOMP version 1.1 and refined in 1.2, provides a mechanism to detect connection failures and prevent idle timeouts imposed by underlying transports. During connection negotiation, both client and server specify heart-beat intervals via the heart-beat header in the format "tx,rx" (e.g., "0,30000" for server-to-client every 30 seconds), where tx indicates the client's transmission interval to the server and rx the server's to the client, both in milliseconds. If enabled, parties send empty frames (a single ) at the negotiated rate when no other data is transmitted; failure to receive heart-beats within a timeout period signals a dead connection, prompting closure. This feature is configurable and optional, with the CONNECTED frame echoing the effective intervals. Security in STOMP connection establishment relies primarily on optional authentication via the login and passcode headers in the CONNECT frame, which the server verifies against its credentials store. Invalid credentials result in an frame and connection rejection. The protocol itself does not provide built-in or advanced ; instead, it depends on the underlying transport for security, such as TLS for and . Version 1.2 introduces improved error handling, including more descriptive frames during connection setup, to enhance robustness without altering core security primitives.

Publishing, Subscribing, and Acknowledgements

In STOMP, publishing messages occurs through the SEND operation, where a client transmits a message to a specified destination, such as a queue or topic, using the required destination header to route the content. The message body carries the payload, while optional headers like content-type and content-length provide metadata for proper handling, and the protocol supports broker-specific extensions for attributes such as expiration and priority to influence message lifecycle and ordering. Subscribing to receive messages involves the SUBSCRIBE operation, in which a client registers interest in messages from a particular destination by specifying both the destination and a unique id for the subscription. The ack header configures the acknowledgment mode: auto (the default), where the server assumes delivery upon sending without client confirmation; client, requiring cumulative acknowledgments from the client for all messages up to a point; or client-individual, mandating per-message acknowledgments. Upon subscription, the server delivers matching messages to the client via MESSAGE frames. Acknowledgements ensure reliable message delivery in manual modes, with clients issuing an frame to confirm processing of a specific identified by its id header from the received MESSAGE frame. In client mode, an cumulatively confirms all unacknowledged preceding the specified one, while in client-individual mode, it applies only to the individual ; conversely, a NACK frame signals rejection, allowing server-specific behaviors like redelivery. The auto mode delegates all acknowledgments to the server, eliminating client involvement. Error handling during publishing or subscribing prompts the server to issue an ERROR frame, which includes a descriptive message header and optional body detailing the issue, such as an invalid destination or malformed request, after which the connection is closed to prevent further operations. Transactions enable atomic grouping of multiple SEND and ACK/NACK operations across a session by initiating with a BEGIN frame using a , followed by the grouped actions, and concluding with either COMMIT to apply all changes or ABORT to roll them back; any uncommitted transaction automatically aborts upon disconnection.

Examples

Basic Send and Receive

The basic send and receive operation in STOMP demonstrates the protocol's core functionality for point-to-point messaging between clients via a broker, using only essential frames over a plain . In this minimal scenario, one client establishes a , publishes a single message to a destination, while another client connects, subscribes to the same destination in auto-ack (where messages are automatically acknowledged upon ), and consumes the message. No , transactions, or advanced headers are involved, assuming a broker that supports STOMP version 1.2 and defaults to auto-acknowledgment for subscriptions. STOMP communication relies on text-based , each consisting of a command line, optional headers, a body (if present), and a octet (0x00) . The process begins with the publishing client sending a CONNECT frame to negotiate the and , followed by the broker's CONNECTED response confirming the session. The publishing client then issues a SEND frame specifying the destination and message body. Here is the verbatim frame sequence for the publishing client:
CONNECT
accept-version:1.2
host:example-broker

\x00

CONNECTED
version:1.2

\x00

SEND
destination:/queue/foo

Hello
\x00
The \x00 represents the null terminator required at the end of each frame. Concurrently, the consuming client follows a similar connection sequence but adds a SUBSCRIBE frame to register interest in the /queue/foo destination, using auto-ack mode (the default if no ack header is specified). Upon receiving the subscription, the broker delivers the pending message via a MESSAGE frame, which includes the destination and body for the consumer to process. The corresponding frames for the consuming client are:
CONNECT
accept-version:1.2
host:example-broker

\x00

CONNECTED
version:1.2

\x00

SUBSCRIBE
id:sub-001
destination:/queue/foo

\x00

MESSAGE
subscription:sub-001
destination:/queue/foo
message-id:msg-001

Hello
\x00
This exchange ensures reliable delivery of the "Hello" message from publisher to consumer without explicit acknowledgments, highlighting STOMP's simplicity for basic queuing.

Subscription and Transaction Example

In the Streaming Text Oriented Messaging Protocol (STOMP), a client can subscribe to a destination with manual acknowledgements to ensure reliable , while allow grouping multiple operations atomically. Consider a where a client subscribes to a for processing messages, manually acknowledges each upon successful handling, and then uses a to batch-send multiple related messages before committing them to . This approach guarantees that acknowledgements and sends either all succeed or all fail, preventing partial updates in distributed systems. The subscription begins with a SUBSCRIBE frame specifying the ack:client mode, which requires the client to explicitly acknowledge each received message; unacknowledged messages may be redelivered by . The frame includes required headers such as id for the subscription identifier and destination for the target queue or topic. For example:
SUBSCRIBE
id:sub-123
destination:/queue/orders
ack:client
receipt: sub-receipt
Upon subscription, the broker sends MESSAGE frames to the client, each including a subscription header matching the subscription id, a unique message-id or ack header for acknowledgement, and the destination. The client processes the message and sends an frame referencing the ack value to confirm . To incorporate transactions, the client first issues a BEGIN frame with a transaction header, then includes the same transaction header in subsequent and SEND frames, before finalizing with a COMMIT frame. This ensures all operations within the transaction are processed atomically. An optional receipt header in SEND frames can solicit confirmation from the broker. A sample sequence for receiving one message and batch-sending two in a transaction follows:
SUBSCRIBE
id:sub-123
destination:/queue/orders
ack:client
receipt: sub-receipt

[Broker response: RECEIPT\nreceipt-id:sub-receipt\n\n\0]

MESSAGE
subscription:sub-123
message-id:msg-456
destination:/queue/orders
ack:ack-456
content-length:15

Order processed
\0

BEGIN
transaction:tx-789
receipt: begin-receipt

ACK
id:ack-456
transaction:tx-789

SEND
destination:/queue/confirmations
transaction:tx-789
receipt: send1-receipt

Confirmation 1
\0

SEND
destination:/queue/confirmations
transaction:tx-789
receipt: send2-receipt

Confirmation 2
\0

COMMIT
transaction:tx-789
receipt: commit-receipt
[Broker responses: RECEIPT frames for each receipt header, confirming subscription, begins, sends, and commit.] In client-ack mode, if a message cannot be processed, the client sends a NACK frame with the message's id instead of ACK, potentially triggering redelivery to the same or another client, depending on broker policy. For instance:
NACK
id:ack-456
transaction:tx-789
This rejects the message within the , allowing via ABORT if needed, ensuring no partial commitments occur.

Implementations

Message Broker Support

provides comprehensive support for the STOMP protocol in both its Classic and Artemis distributions. ActiveMQ Classic enables STOMP over and includes a JMS mapping that allows STOMP clients to interact with JMS destinations, facilitating interoperability with applications. ActiveMQ Artemis, the modern successor, fully implements STOMP versions 1.0, 1.1, and 1.2 over and WebSockets, with features like heart-beating, large message handling, and JMS bridging for enhanced reliability and cross-protocol communication. RabbitMQ integrates STOMP via a core plugin that supports versions 1.0 through 1.2, including extensions for durable subscriptions and error receipts. This plugin maps STOMP destinations to underlying AMQP 0.9.1 exchanges and queues, enabling STOMP clients to leverage RabbitMQ's robust routing and clustering capabilities. Additionally, the Web STOMP plugin extends support over WebSockets, allowing direct connectivity from web browsers without polling. Apache Apollo, a legacy message broker based on the original ActiveMQ foundations, offers native STOMP support for versions 1.0 to 1.2, with built-in transport for simplified browser integration. Designed for high performance and ease of maintenance, Apollo emphasizes STOMP as a core protocol but has been superseded by ActiveMQ since its last major release in 2015. As the successor to HornetQ, Apache ActiveMQ inherits and expands STOMP compatibility through configurable acceptors that handle versions 1.0 to 1.2. HornetQ originally provided STOMP for cross-language clients, including support for JMS-like semantics, which Artemis enhances with non-blocking architecture and multi-protocol bridging. IBM MQ does not natively support STOMP and typically requires third-party adapters or bridges for integration in specific scenarios. As of 2025, STOMP maintains relevance in message brokers for applications due to its , text-oriented design that simplifies integration with resource-constrained devices, though many brokers prioritize AMQP and MQTT for superior efficiency in high-volume, bandwidth-limited environments.

Client Libraries and Tools

Several client libraries implement the STOMP protocol across various programming languages, facilitating connections to message brokers over or transports. These libraries emphasize simplicity and interoperability, allowing developers to publish and subscribe to messages with minimal . Many support STOMP versions 1.0 through 1.2, including features like heart-beating for connection health monitoring and frame acknowledgments. In , the provides a robust STOMP client via its and modules, enabling seamless integration in enterprise applications for real-time messaging over WebSockets. This client supports automatic reconnection and SockJS fallback for browser compatibility. In October 2025, versions 6.2.12, 6.1.24, and 5.3.46 addressed a STOMP over CSRF vulnerability (CVE-2025-41254). Additionally, offers STOMP support through its mapping, allowing Java developers to use familiar JMS APIs while communicating via STOMP frames. For environments, including browsers and , the delivers a full-featured STOMP client optimized for connections, supporting versions 1.0 to 1.2 with built-in heart-beating and broker-specific extensions like compatibility. It simplifies client-side development by handling framing and error recovery automatically. Python developers can use stomp.py, a comprehensive that implements the full STOMP over both and , including support for transactions and receipts. It includes a built-in for quick testing. Other languages offer dedicated STOMP clients as well. The Ruby stomp gem provides an easy-to-use interface for connecting to brokers, supporting STOMP 1.0 to 1.2 with asynchronous message handling. In Perl, Net::STOMP::Client offers object-oriented access to STOMP operations, compatible with versions up to 1.2. For Go, the stompngo package implements a lightweight client with full protocol adherence, including 1.2 features like enhanced error frames. In C#, NMS.ActiveMQ serves as a JMS-like client for STOMP, enabling .NET applications to interact with brokers via TCP or SSL-secured connections. Command-line tools enhance testing and debugging of STOMP interactions. Stompcat, bundled with the Ruby stomp gem, acts as a versatile CLI client akin to , allowing users to send and receive STOMP messages interactively over or .

References

  1. [1]
    STOMP Protocol Specification, Version 1.2
    Abstract. STOMP is a simple interoperable protocol designed for asynchronous message passing between clients via mediating servers. It defines a text based ...Overview · STOMP Frames · Connecting · Client Frames
  2. [2]
    STOMP
    STOMP is the Simple (or Streaming) Text Orientated Messaging Protocol. STOMP provides an interoperable wire format so that STOMP clients can communicate with ...1.2 · Implementations · 1.1
  3. [3]
    Overview :: Spring Framework
    Feb 6, 2012 · STOMP can be used over any reliable two-way streaming network protocol, such as TCP and WebSocket. Although STOMP is a text-oriented protocol, ...
  4. [4]
    STOMP - ActiveMQ
    STOMP is a text-orientated wire protocol that allows STOMP clients to communicate with STOMP Brokers. Apache ActiveMQ Artemis supports STOMP 1.0, 1.1 and 1.2.Missing: history | Show results with:history
  5. [5]
    [PDF] STOMP: A Software Architecture for the Design and Simulation UAV ...
    STOMP operates at the application layer, while Mobile Mesh operates at layer 3 of the OSI model, thus STOMP remains independent of the specific communication ...
  6. [6]
    STOMP Protocol - GeeksforGeeks
    Sep 11, 2023 · STOMP is the Simple (or Streaming) Text Orientated Messaging Protocol, formerly known as TTMP. It provides an interoperable wire format that allows STOMP ...
  7. [7]
    Stomp Protocol Specification, Version 1.0
    The frame starts with a command (in this case CONNECT), followed by a newline, followed by headers in a <key> : <value> with each header followed by a newline.Missing: history | Show results with:history
  8. [8]
    STOMP Protocol Specification, Version 1.1
    Changes in the Protocol​​ STOMP 1.1 is designed to be backwards compatible with STOMP 1.0 while introducing several new features not present in STOMP 1.0: ...Overview · STOMP Frames · Protocol Negotiation · Client Frames
  9. [9]
    Stomp protocol history and owner - Stack Overflow
    Oct 28, 2012 · Have you read through the official STOMP website ? The Spec are located on the site, v1.0, v1.1 and v1.2 (latest). Releases are done via a ...
  10. [10]
    Why Use STOMP? - Zymr
    Aug 4, 2024 · STOMP offers excellent scalability and performance characteristics, making it ideal for applications that require real-time messaging at scale. ...<|control11|><|separator|>
  11. [11]
    Stomp - ActiveMQ - The Apache Software Foundation
    The STOMP protocol (version 1.1 or greater) defines the concept of heart beats as a method by which a client and broker can determine the health of the ...Missing: origins date
  12. [12]
    STOMP Plugin - RabbitMQ
    The plugin supports STOMP versions 1.0 through 1.2 with some extensions and restrictions. STOMP clients can interoperate with other protocols. All the ...
  13. [13]
    RabbitMQ Web STOMP Plugin
    The Web STOMP plugin makes it possible to use STOMP over a WebSocket connection. The goal of this plugin is to enable STOMP messaging in Web applications.
  14. [14]
    Apollo 1.7.1 STOMP Protocol Manual - ActiveMQ
    STOMP provides an interoperable messaging wire level protocol that allows any STOMP clients can communicate with any STOMP message broker. It aims to ...
  15. [15]
    Chapter 48. Interoperability - JBoss.org
    With Stomp 1.1 users can use heart-beats to maintain the life cycle of stomp connections. ... Stomp broker and proxy the Stomp protocol to the standard JMS API.<|control11|><|separator|>
  16. [16]
  17. [17]
    APIs and Protocol Support - Solace
    Solace makes it easy to connect all kinds of applications to your event mesh with a rich set of APIs, support for all popular protocols, and packaged micro- ...
  18. [18]
  19. [19]
    Implementations - STOMP
    STOMP Servers​​ Apache ActiveMQ Artemis has a proven non blocking architecture. It delivers outstanding performance.<|control11|><|separator|>
  20. [20]
    stomp-js/stompjs: Javascript and Typescript Stomp client for ... - GitHub
    Simple and intuitive API for interacting with the STOMP protocol · Support for STOMP protocol versions: 1.2, 1.1, and 1.0 · Support for fallback options when ...
  21. [21]
  22. [22]
  23. [23]
    Net::STOMP::Client - MetaCPAN
    This module provides an object oriented client interface to interact with servers supporting STOMP (Streaming Text Orientated Messaging Protocol).Missing: stompcat | Show results with:stompcat<|separator|>
  24. [24]
  25. [25]