Skip to content

Commit 8670411

Browse files
committed
Update docs on stomp.js library
This is a backport of #357fb4. Issue: SPR-15624
1 parent d62dd2d commit 8670411

File tree

1 file changed

+110
-86
lines changed

1 file changed

+110
-86
lines changed

src/asciidoc/web-websocket.adoc

Lines changed: 110 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,11 +1017,11 @@ Consider also customizing these server-side SockJS related properties (see Javad
10171017
== STOMP
10181018

10191019
The WebSocket protocol defines two types of messages, text and binary, but their
1020-
content is undefined. It's expected that the client and server may agree on using
1021-
a sub-protocol (i.e. a higher-level protocol) to define message semantics.
1022-
While the use of a sub-protocol with WebSocket is completely optional either way
1023-
client and server will need to agree on some kind of protocol to help interpret
1024-
messages.
1020+
content is undefined. The defines a mechanism for client and server to negotiate a
1021+
sub-protocol -- i.e. a higher level messaging protocol, to use on top of WebSocket to
1022+
define what kind of messages each can send, what is the format and content for each
1023+
message, and so on. The use of a sub-protocol is optional but either way client and
1024+
server will need to agree on some protocol that defines message content.
10251025

10261026

10271027

@@ -1118,29 +1118,34 @@ The above overview is intended to provide the most basic understanding of the
11181118
STOMP protocol. It is recommended to review the protocol
11191119
http://stomp.github.io/stomp-specification-1.2.html[specification] in full.
11201120

1121-
The benefits of using STOMP as a WebSocket sub-protocol:
11221121

1123-
* No need to invent a custom message format
1124-
* Use existing https://github.com/jmesnil/stomp-websocket[stomp.js] client in the browser
1125-
* Ability to route messages to based on destination
1126-
* Option to use full-fledged message broker such as RabbitMQ, ActiveMQ, etc. for broadcasting
11271122

1128-
Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework
1129-
to provide a programming model for application-level use in the same way that
1130-
Spring MVC provides a programming model based on HTTP.
1123+
[[websocket-stomp-benefits]]
1124+
=== Benefits
1125+
1126+
Use of STOMP as a sub-protocol enables the Spring Framework and Spring Security to
1127+
provide a richer programming model vs using raw WebSockets. The same point can be
1128+
made about how HTTP vs raw TCP and how it enables Spring MVC and other web frameworks
1129+
to provide rich functionality. The following is a list of benefits:
1130+
1131+
* No need to invent a custom messaging protocol and message format.
1132+
* STOMP clients are available including a <<websocket-stomp-client,Java client>>
1133+
in the Spring Framework.
1134+
* Message brokers such as RabbitMQ, ActiveMQ, and others can be used (optionally) to
1135+
manage subscriptions and broadcast messages.
1136+
* Application logic can be organized in any number of ``@Controller``'s and messages
1137+
routed to them based on the STOMP destination header vs handling raw WebSocket messages
1138+
with a single `WebSocketHandler` for a given connection.
1139+
* Use Spring Security to secure messages based on STOMP destinations and message types.
11311140

11321141

11331142

11341143
[[websocket-stomp-enable]]
11351144
=== Enable STOMP
11361145

1137-
The Spring Framework provides support for using STOMP over WebSocket through
1138-
the +spring-messaging+ and +spring-websocket+ modules.
1139-
Here is an example of exposing a STOMP WebSocket/SockJS endpoint at the URL path
1140-
`/portfolio` where messages whose destination starts with "/app" are routed to
1141-
message-handling methods (i.e. application work) and messages whose destinations
1142-
start with "/topic" or "/queue" will be routed to the message broker (i.e.
1143-
broadcasting to other connected clients):
1146+
STOMP over WebSocket support is available in the `spring-messaging` and the
1147+
`spring-websocket` modules. Once you have those dependencies, you can expose a STOMP
1148+
endpoints, over WebSocket with <<websocket-fallback>>, as shown below:
11441149

11451150
[source,java,indent=0]
11461151
[subs="verbatim,quotes"]
@@ -1165,7 +1170,14 @@ broadcasting to other connected clients):
11651170
}
11661171
----
11671172

1168-
and in XML:
1173+
<1> `"/portfolio"` is the HTTP URL for the endpoint to which a WebSocket (or SockJS)
1174+
client will need to connect to for the WebSocket handshake.
1175+
<2> STOMP messages whose destination header begins with `"/app"` are routed to
1176+
`@MessageMapping` methods in `@Controller` classes.
1177+
<3> Use the built-in, message broker for subscriptions and broadcasting;
1178+
Route messages whose destination header begins with "/topic" or "/queue" to the broker.
1179+
1180+
The same configuration in XML:
11691181

11701182
[source,xml,indent=0]
11711183
[subs="verbatim,quotes,attributes"]
@@ -1191,30 +1203,27 @@ and in XML:
11911203

11921204
[NOTE]
11931205
====
1194-
The "/app" prefix is arbitrary. You can pick any prefix. It's simply meant to differentiate
1195-
messages to be routed to message-handling methods to do application work vs messages
1196-
to be routed to the broker to broadcast to subscribed clients.
1197-
1198-
The "/topic" and "/queue" prefixes depend on the broker in use. In the case of the simple,
1199-
in-memory broker the prefixes do not have any special meaning; it's merely a convention
1200-
that indicates how the destination is used (pub-sub targetting many subscribers or
1201-
point-to-point messages typically targeting an individual recipient).
1202-
In the case of using a dedicated broker, most brokers use "/topic" as
1203-
a prefix for destinations with pub-sub semantics and "/queue" for destinations
1204-
with point-to-point semantics. Check the STOMP page of the broker to see the destination
1205-
semantics it supports.
1206+
For the built-in, simple broker the "/topic" and "/queue" prefixes do not have any special
1207+
meaning. They're merely a convention to differentiate between pub-sub vs point-to-point
1208+
messaging (i.e. many subscribers vs one consumer). When using an external broker, please
1209+
check the STOMP page of the broker to understand what kind of STOMP destinations and
1210+
prefixes it supports.
12061211
====
12071212

1208-
1209-
On the browser side, a client might connect as follows using
1210-
https://github.com/jmesnil/stomp-websocket[stomp.js] and the
1211-
https://github.com/sockjs/sockjs-client[sockjs-client]:
1213+
To connect from a browser, for SockJS you can use the
1214+
https://github.com/sockjs/sockjs-client[sockjs-client]. For STOMP many applications have
1215+
used the https://github.com/jmesnil/stomp-websocket[jmesnil/stomp-websocket] library
1216+
(also known as stomp.js) which is feature complete and has been used in production for
1217+
years but is no longer maintained. At present the
1218+
https://github.com/JSteunou/webstomp-client[JSteunou/webstomp-client] is the most
1219+
actively maintained and evolving successor of that library and the example code below
1220+
is based on it:
12121221

12131222
[source,javascript,indent=0]
12141223
[subs="verbatim,quotes"]
12151224
----
12161225
var socket = new SockJS("/spring-websocket-portfolio/portfolio");
1217-
var stompClient = Stomp.over(socket);
1226+
var stompClient = webstomp.over(socket);
12181227
12191228
stompClient.connect({}, function(frame) {
12201229
}
@@ -1237,65 +1246,76 @@ Even if it did, they would be ignored, or rather overridden, on the server side.
12371246
sections <<websocket-stomp-handle-broker-relay-configure>> and
12381247
<<websocket-stomp-authentication>> for more information on authentication.
12391248

1249+
For a more example code see:
1250+
1251+
* https://spring.io/guides/gs/messaging-stomp-websocket/[Using WebSocket to build an
1252+
interactive web application] getting started guide.
1253+
* https://github.com/rstoyanchev/spring-websocket-portfolio[Stock Portfolio] sample
1254+
application.
1255+
12401256

12411257

12421258
[[websocket-stomp-message-flow]]
12431259
=== Flow of Messages
12441260

1245-
When a STOMP endpoint is configured, the Spring application acts as the STOMP broker
1246-
to connected clients. This section provides a big picture overview of how messages flow
1247-
within the application.
1261+
Once a STOMP endpoint is exposed, the Spring application becomes a STOMP broker for
1262+
connected clients. This section describes the flow of messages on the server side.
12481263

1249-
The `spring-messaging` module provides the foundation for asynchronous message processing.
1250-
It contains a number of abstractions that originated in the
1251-
https://spring.io/spring-integration[Spring Integration] project and are intended
1252-
for use as building blocks in messaging applications:
1264+
The `spring-messaging` module contains foundational support for messaging applications
1265+
that originated in https://spring.io/spring-integration[Spring Integration] and was
1266+
later extracted and incorporated into the Spring Framework for broader use across many
1267+
https://spring.io/projects[Spring projects] and application scenarios.
1268+
Below is a list of a few of the available messaging abstractions:
12531269

12541270
* {api-spring-framework}/messaging/Message.html[Message] --
1255-
a message with headers and a payload.
1271+
simple representation for a message including headers and payload.
12561272
* {api-spring-framework}/messaging/MessageHandler.html[MessageHandler] --
1257-
a contract for handling a message.
1273+
contract for handling a message.
12581274
* {api-spring-framework}/messaging/MessageChannel.html[MessageChannel] --
1259-
a contract for sending a message enabling loose coupling between senders and receivers.
1275+
contract for sending a message that enables loose coupling between producers and consumers.
12601276
* {api-spring-framework}/messaging/SubscribableChannel.html[SubscribableChannel] --
1261-
extends `MessageChannel` and sends messages to registered `MessageHandler` subscribers.
1277+
`MessageChannel` with `MessageHandler` subscribers.
12621278
* {api-spring-framework}/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel] --
1263-
a concrete implementation of `SubscribableChannel` that can deliver messages
1264-
asynchronously via a thread pool.
1279+
`SubscribableChannel` that uses an `Executor` for delivering messages.
12651280

1266-
The `@EnableWebSocketMessageBroker` Java config and the `<websocket:message-broker>` XML config
1267-
both assemble a concrete message flow. Below is a diagram of the part of the setup when using
1268-
the simple, in-memory broker:
1281+
Both the Java config (i.e. `@EnableWebSocketMessageBroker`) and the XML namespace config
1282+
(i.e. `<websocket:message-broker>`) use the above components to assemble a message
1283+
workflow. The diagram below shows the components used when the simple, built-in message
1284+
broker is enabled:
12691285

12701286
image::images/message-flow-simple-broker.png[width=640]
12711287

1272-
The above setup that includes 3 message channels:
1288+
There are 3 message channels in the above diagram:
12731289

1274-
* `"clientInboundChannel"` for messages from WebSocket clients.
1275-
* `"clientOutboundChannel"` for messages to WebSocket clients.
1276-
* `"brokerChannel"` for messages to the broker from within the application.
1290+
* `"clientInboundChannel"` -- for passing messages received from WebSocket clients.
1291+
* `"clientOutboundChannel"` -- for sending server messages to WebSocket clients.
1292+
* `"brokerChannel"` -- for sending messages to the message broker from within
1293+
server-side, application code.
12771294

1278-
The same three channels are also used with a dedicated broker except here a
1279-
"broker relay" takes the place of the simple broker:
1295+
The next diagram shows the components used when an external broker (e.g. RabbitMQ)
1296+
is configured for managing subscriptions and broadcasting messages:
12801297

12811298
image::images/message-flow-broker-relay.png[width=640]
12821299

1283-
Messages on the `"clientInboundChannel"` can flow to annotated
1284-
methods for application handling (e.g. a stock trade execution request) or can
1285-
be forwarded to the broker (e.g. client subscribing for stock quotes).
1286-
The STOMP destination is used for simple prefix-based routing. For example
1287-
the "/app" prefix could route messages to annotated methods while the "/topic"
1288-
and "/queue" prefixes could route messages to the broker.
1300+
The main difference in the above diagram is the use of the "broker relay" for passing
1301+
messages up to the external STOMP broker over TCP, and for passing messages down from the
1302+
broker to subscribed clients.
12891303

1290-
When a message-handling annotated method has a return type, its return
1291-
value is sent as the payload of a Spring `Message` to the `"brokerChannel"`.
1292-
The broker in turn broadcasts the message to clients. Sending a message
1293-
to a destination can also be done from anywhere in the application with
1294-
the help of a messaging template. For example, an HTTP POST handling method
1295-
can broadcast a message to connected clients, or a service component may
1296-
periodically broadcast stock quotes.
1304+
When messages are received from a WebSocket connectin, they're decoded to STOMP frames,
1305+
then turned into a Spring `Message` representation, and sent to the
1306+
`"clientInboundChannel"` for further processing. For example STOMP messages whose
1307+
destination header starts with `"/app"` may be routed to `@MessageMapping` methods in
1308+
annotated controllers, while `"/topic"` and `"/queue"` messages may be routed directly
1309+
to the message broker.
12971310

1298-
Below is a simple example to illustrate the flow of messages:
1311+
An annotated `@Controller` handling a STOMP message from a client may send a message to
1312+
the message broker through the `"brokerChannel"`, and the broker will broadcast the
1313+
message to matching subscribers through the `"clientOutboundChannel"`. The same
1314+
controller can also do the same in response to HTTP requests, so a client may perform an
1315+
HTTP POST and then an `@PostMapping` method can send a message to the message broker
1316+
to broadcast to subscribed clients.
1317+
1318+
Let's trace the flow through a simple example. Given the following server setup:
12991319

13001320
[source,java,indent=0]
13011321
[subs="verbatim,quotes"]
@@ -1329,18 +1349,22 @@ Below is a simple example to illustrate the flow of messages:
13291349
13301350
----
13311351

1332-
The following explains the message flow for the above example:
1333-
1334-
* WebSocket clients connect to the WebSocket endpoint at "/portfolio".
1335-
* Subscriptions to "/topic/greeting" pass through the "clientInboundChannel"
1336-
and are forwarded to the broker.
1337-
* Greetings sent to "/app/greeting" pass through the "clientInboundChannel"
1338-
and are forwarded to the `GreetingController`. The controller adds the current
1339-
time, and the return value is passed through the "brokerChannel" as a message
1340-
to "/topic/greeting" (destination is selected based on a convention but can be
1341-
overridden via `@SendTo`).
1342-
* The broker in turn broadcasts messages to subscribers, and they pass through
1343-
the `"clientOutboundChannel"`.
1352+
. Client connects to `"http://localhost:8080/portfolio"` and once a WebSocket connection
1353+
is established, STOMP frames begin to flow on it.
1354+
. Client sends SUBSCRIBE frame with destination header `"/topic/greeting"`. Once received
1355+
and decoded, the message is sent to the `"clientInboundChannel"`, then routed to the
1356+
message broker which stores the client subscription.
1357+
. Client sends SEND frame to `"/app/greeting"`. The `"/app"` prefix helps to route it to
1358+
annotated controllers. After the `"/app"` prefix is stripped, the remaining `"/greeting"`
1359+
part of the destination is mapped to the `@MessageMapping` method in `GreetingController`.
1360+
. The value returned from `GreetingController` is turned into a Spring `Message` with
1361+
a payload based on the return value and a default destination header of
1362+
`"/topic/greeting"` (derived from the input destination with `"/app"` replaced by
1363+
`"/topic"`). The resulting message is sent to the "brokerChannel" and handled
1364+
by the message broker.
1365+
. The message broker finds all matching subscribers, and sends a MESSAGE frame to each
1366+
through the `"clientOutboundChannel"` from where messages are encoded as STOMP frames
1367+
and sent on the WebSocket connection.
13441368

13451369
The next section provides more details on annotated methods including the
13461370
kinds of arguments and return values supported.

0 commit comments

Comments
 (0)