diff --git a/src/asciidoc/images/message-flow-broker-relay.png b/src/asciidoc/images/message-flow-broker-relay.png
new file mode 100644
index 0000000000..3cf93fa143
Binary files /dev/null and b/src/asciidoc/images/message-flow-broker-relay.png differ
diff --git a/src/asciidoc/images/message-flow-simple-broker.png b/src/asciidoc/images/message-flow-simple-broker.png
new file mode 100644
index 0000000000..9afd54f57c
Binary files /dev/null and b/src/asciidoc/images/message-flow-simple-broker.png differ
diff --git a/src/asciidoc/web-websocket.adoc b/src/asciidoc/web-websocket.adoc
index c702003571..6acdc1731e 100644
--- a/src/asciidoc/web-websocket.adoc
+++ b/src/asciidoc/web-websocket.adoc
@@ -984,11 +984,12 @@ public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport
[[websocket-stomp]]
== STOMP Over WebSocket Messaging Architecture
-The WebSocket protocol defines two main types of messages -- text and binary --
-but leaves their content undefined. Instead it's expected that the client and
-server may agree on using a sub-protocol, i.e. a higher-level protocol that defines
-the message content. Using a sub-protocol is optional but either way the client
-and server both need to understand how to interpret messages.
+The WebSocket protocol defines two types of messages, text and binary, but their
+content is undefined. It's expected that the client and server may agree on using
+a sub-protocol (i.e. a higher-level protocol) to define message semantics.
+While the use of a sub-protocol with WebSocket is completely optional either way
+client and server will need to agree on some kind of protocol to help interpret
+messages.
@@ -996,12 +997,14 @@ and server both need to understand how to interpret messages.
=== Overview of STOMP
http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple
text-oriented messaging protocol that was originally created for scripting languages
-(such as Ruby, Python, and Perl) to connect to enterprise message brokers. It is
-designed to address a subset of commonly used patterns in messaging protocols. STOMP
-can be used over any reliable 2-way streaming network protocol such as TCP and WebSocket.
+such as Ruby, Python, and Perl to connect to enterprise message brokers. It is
+designed to address a subset of commonly used messaging patterns. STOMP can be
+used over any reliable 2-way streaming network protocol such as TCP and WebSocket.
+Although STOMP is a text-oriented protocol, the payload of messages can be
+either text or binary.
-STOMP is a frame based protocol with frames modeled on HTTP. This is the
-structure of a frame:
+STOMP is a frame based protocol whose frames are modeled on HTTP. The structure
+of a STOMP frame:
----
COMMAND
@@ -1011,12 +1014,39 @@ header2:value2
Body^@
----
-For example, a client can use the +SEND+ command to send a message or the
-+SUBSCRIBE+ command to express interest in receiving messages. Both of these commands
-require a +"destination"+ header that indicates where to send a message, or likewise
-what to subscribe to.
+Clients can use the +SEND+ or +SUBSCRIBE+ commands to send or subscribe for
+messages along with a +"destination"+ header that describes what the
+message is about and who should receive it. This enables a simple
+publish-subscribe mechanism that can be used to send messages through the broker
+to other connected clients or to send messages to the server to request that
+some work be performed.
-Here is an example of a client sending a request to buy stock shares:
+When using Spring's STOMP support, the Spring WebSocket application acts
+as the STOMP broker to clients. Messages are routed to `@Controller` message-handling
+methods or to a simple, in-memory broker that keeps track of subscriptions and
+broadcasts messages to subscribed users. You can also configure Spring to work
+with a dedicated STOMP broker (e.g. RabbitMQ, ActiveMQ, etc) for the actual
+broadcasting of messages. In that case Spring maintains
+TCP connections to the broker, relays messages to it, and also passes messages
+from it down to connected WebSocket clients. Thus Spring web applications can
+rely on unified HTTP-based security, common validation, and a familiar programming
+model message-handling work.
+
+Here is an example of a client subscribing to receive stock quotes which
+the server may emit periodically e.g. via a scheduled task sending messages
+through a `SimpMessagingTemplate` to the broker:
+
+----
+SUBSCRIBE
+id:sub-1
+destination:/topic/price.stock.*
+
+^@
+----
+
+Here is an example of a client sending a trade request, which the server
+may handle through an `@MessageMapping` method and later on, after the execution,
+broadcast a trade confirmation message and details down to the client:
----
SEND
@@ -1027,24 +1057,12 @@ content-length:44
{"action":"BUY","ticker":"MMM","shares",44}^@
----
-Here is an example of a client subscribing to receive stock quotes:
-----
-SUBSCRIBE
-id:sub-1
-destination:/topic/price.stock.*
-
-^@
-----
-
-[NOTE]
-====
The meaning of a destination is intentionally left opaque in the STOMP spec. It can
be any string, and it's entirely up to STOMP servers to define the semantics and
the syntax of the destinations that they support. It is very common, however, for
destinations to be path-like strings where `"/topic/.."` implies publish-subscribe
(__one-to-many__) and `"/queue/"` implies point-to-point (__one-to-one__) message
exchanges.
-====
STOMP servers can use the +MESSAGE+ command to broadcast messages to all subscribers.
Here is an example of a server sending a stock quote to a subscribed client:
@@ -1058,26 +1076,21 @@ destination:/topic/price.stock.MMM
{"ticker":"MMM","price":129.45}^@
----
-[NOTE]
-====
It is important to know that a server cannot send unsolicited messages. All messages
from a server must be in response to a specific client subscription, and the
+"subscription-id"+ header of the server message must match the +"id"+ header of the
client subscription.
-====
The above overview is intended to provide the most basic understanding of the
STOMP protocol. It is recommended to review the protocol
-http://stomp.github.io/stomp-specification-1.2.html[specification], which is
-easy to follow and manageable in terms of size.
+http://stomp.github.io/stomp-specification-1.2.html[specification] in full.
-The following summarizes the benefits for an application of using STOMP over WebSocket:
+The benefits of using STOMP as a WebSocket sub-protocol:
-* Standard message format
-* Application-level protocol with support for common messaging patterns
-* Client-side support, e.g. https://github.com/jmesnil/stomp-websocket[stomp.js], https://github.com/cujojs/msgs[msgs.js]
-* The ability to interpret, route, and process messages on both the client and server-side
-* The option to plug in a message broker -- RabbitMQ, ActiveMQ, many others -- to broadcast messages (explained later)
+* No need to invent a custom message format
+* Use existing https://github.com/jmesnil/stomp-websocket[stomp.js] client in the browser
+* Ability to route messages to based on destination
+* Option to use full-fledged message broker such as RabbitMQ, ActiveMQ, etc. for broadcasting
Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework
to provide a programming model for application-level use in the same way that
@@ -1088,10 +1101,12 @@ Spring MVC provides a programming model based on HTTP.
[[websocket-stomp-enable]]
=== Enable STOMP over WebSocket
The Spring Framework provides support for using STOMP over WebSocket through
-the +spring-messaging+ and +spring-websocket+ modules. It's easy to enable it.
-
-Here is an example of configuring a STOMP WebSocket endpoint with SockJS fallback
-options. The endpoint is available for clients to connect to a URL path `/portfolio`:
+the +spring-messaging+ and +spring-websocket+ modules.
+Here is an example of exposing a STOMP WebSocket/SockJS endpoint at the URL path
+`/portfolio` where messages whose destination starts with "/app" are routed to
+message-handling methods (i.e. application work) and messages whose destinations
+start with "/topic" or "/queue" will be routed to the message broker (i.e.
+broadcasting to other connected clients):
[source,java,indent=0]
[subs="verbatim,quotes"]
@@ -1103,23 +1118,21 @@ options. The endpoint is available for clients to connect to a URL path `/portfo
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
- @Override
- public void configureMessageBroker(MessageBrokerRegistry config) {
- config.setApplicationDestinationPrefixes("/app");
- config.enableSimpleBroker("/queue", "/topic");
- }
-
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").withSockJS();
}
- // ...
+ @Override
+ public void configureMessageBroker(MessageBrokerRegistry config) {
+ config.setApplicationDestinationPrefixes("/app");
+ config.enableSimpleBroker("/topic", "/queue");
+ }
}
----
-XML configuration equivalent:
+and in XML:
[source,xml,indent=0]
[subs="verbatim,quotes,attributes"]
@@ -1137,13 +1150,29 @@ XML configuration equivalent:
-
- ...
+
----
+[NOTE]
+====
+The "/app" prefix is arbitrary. You can pick any prefix. It's simply meant to differentiate
+messages to be routed to message-handling methods to do application work vs messages
+to be routed to the broker to broadcast to subscribed clients.
+
+The "/topic" and "/queue" prefixes depend on the broker in use. In the case of the simple,
+in-memory broker the prefixes do not have any special meaning; it's merely a convention
+that indicates how the destination is used (pub-sub targetting many subscribers or
+point-to-point messages typically targeting an individual recipient).
+In the case of using a dedicated broker, most brokers use "/topic" as
+a prefix for destinations with pub-sub semantics and "/queue" for destinations
+with point-to-point semantics. Check the STOMP page of the broker to see the destination
+semantics it supports.
+====
+
+
On the browser side, a client might connect as follows using
https://github.com/jmesnil/stomp-websocket[stomp.js] and the
https://github.com/sockjs/sockjs-client[sockjs-client]:
@@ -1180,10 +1209,11 @@ sections <> and
=== Flow of Messages
When a STOMP endpoint is configured, the Spring application acts as the STOMP broker
-to connected clients. It handles incoming messages and sends messages back.
-This section provides a big picture overview of how messages flow inside the application.
+to connected clients. This section provides a big picture overview of how messages flow
+within the application.
-The `spring-messaging` module contains a number of abstractions that originated in the
+The `spring-messaging` module provides the foundation for asynchronous message processing.
+It contains a number of abstractions that originated in the
https://spring.io/spring-integration[Spring Integration] project and are intended
for use as building blocks in messaging applications:
@@ -1199,16 +1229,22 @@ extends `MessageChannel` and sends messages to registered `MessageHandler` subsc
a concrete implementation of `SubscribableChannel` that can deliver messages
asynchronously via a thread pool.
-The provided STOMP over WebSocket config, both Java and XML, uses the above to
-assemble a concrete message flow including the following 3 channels:
+The `@EnableWebSocketMessageBroker` Java config and the `` XML config
+both assemble a concrete message flow. Below is a diagram of the part of the setup when using
+the simple, in-memory broker:
-* `"clientInboundChannel"` -- for messages from WebSocket clients. Every incoming
-WebSocket message carrying a STOMP frame is sent through this channel.
-* `"clientOutboundChannel"` -- for messages to WebSocket clients. Every outgoing
-STOMP message from the broker is sent through this channel before getting sent
-to a client's WebSocket session.
-* `"brokerChannel"` -- for messages to the broker from within the application.
-Every message sent from the application to the broker passes through this channel.
+image::images/message-flow-simple-broker.png[width=640]
+
+The above setup that includes 3 message channels:
+
+* `"clientInboundChannel"` for messages from WebSocket clients.
+* `"clientOutboundChannel"` for messages to WebSocket clients.
+* `"brokerChannel"` for messages to the broker from within the application.
+
+The same three channels are also used with a dedicated broker except here a
+"broker relay" takes the place of the simple broker:
+
+image::images/message-flow-broker-relay.png[width=640]
Messages on the `"clientInboundChannel"` can flow to annotated
methods for application handling (e.g. a stock trade execution request) or can