@@ -1017,11 +1017,11 @@ Consider also customizing these server-side SockJS related properties (see Javad
1017
1017
== STOMP
1018
1018
1019
1019
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 .
1025
1025
1026
1026
1027
1027
@@ -1118,29 +1118,34 @@ The above overview is intended to provide the most basic understanding of the
1118
1118
STOMP protocol. It is recommended to review the protocol
1119
1119
http://stomp.github.io/stomp-specification-1.2.html[specification] in full.
1120
1120
1121
- The benefits of using STOMP as a WebSocket sub-protocol:
1122
1121
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
1127
1122
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.
1131
1140
1132
1141
1133
1142
1134
1143
[[websocket-stomp-enable]]
1135
1144
=== Enable STOMP
1136
1145
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:
1144
1149
1145
1150
[source,java,indent=0]
1146
1151
[subs="verbatim,quotes"]
@@ -1165,7 +1170,14 @@ broadcasting to other connected clients):
1165
1170
}
1166
1171
----
1167
1172
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:
1169
1181
1170
1182
[source,xml,indent=0]
1171
1183
[subs="verbatim,quotes,attributes"]
@@ -1191,30 +1203,27 @@ and in XML:
1191
1203
1192
1204
[NOTE]
1193
1205
====
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.
1206
1211
====
1207
1212
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:
1212
1221
1213
1222
[source,javascript,indent=0]
1214
1223
[subs="verbatim,quotes"]
1215
1224
----
1216
1225
var socket = new SockJS("/spring-websocket-portfolio/portfolio");
1217
- var stompClient = Stomp .over(socket);
1226
+ var stompClient = webstomp .over(socket);
1218
1227
1219
1228
stompClient.connect({}, function(frame) {
1220
1229
}
@@ -1237,65 +1246,76 @@ Even if it did, they would be ignored, or rather overridden, on the server side.
1237
1246
sections <<websocket-stomp-handle-broker-relay-configure>> and
1238
1247
<<websocket-stomp-authentication>> for more information on authentication.
1239
1248
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
+
1240
1256
1241
1257
1242
1258
[[websocket-stomp-message-flow]]
1243
1259
=== Flow of Messages
1244
1260
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.
1248
1263
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:
1253
1269
1254
1270
* {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.
1256
1272
* {api-spring-framework}/messaging/MessageHandler.html[MessageHandler] --
1257
- a contract for handling a message.
1273
+ contract for handling a message.
1258
1274
* {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 .
1260
1276
* {api-spring-framework}/messaging/SubscribableChannel.html[SubscribableChannel] --
1261
- extends `MessageChannel` and sends messages to registered `MessageHandler` subscribers.
1277
+ `MessageChannel` with `MessageHandler` subscribers.
1262
1278
* {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.
1265
1280
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:
1269
1285
1270
1286
image::images/message-flow-simple-broker.png[width=640]
1271
1287
1272
- The above setup that includes 3 message channels:
1288
+ There are 3 message channels in the above diagram :
1273
1289
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.
1277
1294
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 :
1280
1297
1281
1298
image::images/message-flow-broker-relay.png[width=640]
1282
1299
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.
1289
1303
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.
1297
1310
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:
1299
1319
1300
1320
[source,java,indent=0]
1301
1321
[subs="verbatim,quotes"]
@@ -1329,18 +1349,22 @@ Below is a simple example to illustrate the flow of messages:
1329
1349
1330
1350
----
1331
1351
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.
1344
1368
1345
1369
The next section provides more details on annotated methods including the
1346
1370
kinds of arguments and return values supported.
0 commit comments