@@ -850,23 +850,23 @@ Consider also customizing these server-side SockJS related properties (see Javad
850
850
== STOMP
851
851
852
852
The WebSocket protocol defines two types of messages, text and binary, but their
853
- content is undefined. It's expected that the client and server may agree on using
854
- a sub-protocol ( i.e. a higher- level protocol) to define message semantics.
855
- While the use of a sub-protocol with WebSocket is completely optional either way
856
- client and server will need to agree on some kind of protocol to help interpret
857
- messages .
853
+ content is undefined. The defines a mechanism for client and server to negotiate a
854
+ sub-protocol -- i.e. a higher level messaging protocol, to use on top of WebSocket to
855
+ define what kind of messages each can send, what is the format and content for each
856
+ message, and so on. The use of a sub-protocol is optional but either way client and
857
+ server will need to agree on some protocol that defines message content .
858
858
859
859
860
860
861
861
[[websocket-stomp-overview]]
862
862
=== Overview
863
863
864
- http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple
864
+ http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple,
865
865
text-oriented messaging protocol that was originally created for scripting languages
866
866
such as Ruby, Python, and Perl to connect to enterprise message brokers. It is
867
- designed to address a subset of commonly used messaging patterns. STOMP can be
868
- used over any reliable 2-way streaming network protocol such as TCP and WebSocket.
869
- Although STOMP is a text-oriented protocol, the payload of messages can be
867
+ designed to address a minimal subset of commonly used messaging patterns. STOMP can be
868
+ used over any reliable, 2-way streaming network protocol such as TCP and WebSocket.
869
+ Although STOMP is a text-oriented protocol, message payloads can be
870
870
either text or binary.
871
871
872
872
STOMP is a frame based protocol whose frames are modeled on HTTP. The structure
@@ -951,29 +951,34 @@ The above overview is intended to provide the most basic understanding of the
951
951
STOMP protocol. It is recommended to review the protocol
952
952
http://stomp.github.io/stomp-specification-1.2.html[specification] in full.
953
953
954
- The benefits of using STOMP as a WebSocket sub-protocol:
955
954
956
- * No need to invent a custom message format
957
- * Use existing https://github.com/jmesnil/stomp-websocket[stomp.js] client in the browser
958
- * Ability to route messages to based on destination
959
- * Option to use full-fledged message broker such as RabbitMQ, ActiveMQ, etc. for broadcasting
960
955
961
- Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework
962
- to provide a programming model for application-level use in the same way that
963
- Spring MVC provides a programming model based on HTTP.
956
+ [[websocket-stomp-benefits]]
957
+ === Benefits
958
+
959
+ Use of STOMP as a sub-protocol enables the Spring Framework and Spring Security to
960
+ provide a richer programming model vs using raw WebSockets. The same point can be
961
+ made about how HTTP vs raw TCP and how it enables Spring MVC and other web frameworks
962
+ to provide rich functionality. The following is a list of benefits:
963
+
964
+ * No need to invent a custom messaging protocol and message format.
965
+ * STOMP clients are available including a <<websocket-stomp-client,Java client>>
966
+ in the Spring Framework.
967
+ * Message brokers such as RabbitMQ, ActiveMQ, and others can be used (optionally) to
968
+ manage subscriptions and broadcast messages.
969
+ * Application logic can be organized in any number of ``@Controller``'s and messages
970
+ routed to them based on the STOMP destination header vs handling raw WebSocket messages
971
+ with a single `WebSocketHandler` for a given connection.
972
+ * Use Spring Security to secure messages based on STOMP destinations and message types.
964
973
965
974
966
975
967
976
[[websocket-stomp-enable]]
968
977
=== Enable STOMP
969
978
970
- The Spring Framework provides support for using STOMP over WebSocket through
971
- the +spring-messaging+ and +spring-websocket+ modules.
972
- Here is an example of exposing a STOMP WebSocket/SockJS endpoint at the URL path
973
- `/portfolio` where messages whose destination starts with "/app" are routed to
974
- message-handling methods (i.e. application work) and messages whose destinations
975
- start with "/topic" or "/queue" will be routed to the message broker (i.e.
976
- broadcasting to other connected clients):
979
+ STOMP over WebSocket support is available in the `spring-messaging` and the
980
+ `spring-websocket` modules. Once you have those dependencies, you can expose a STOMP
981
+ endpoints, over WebSocket with <<websocket-fallback>>, as shown below:
977
982
978
983
[source,java,indent=0]
979
984
[subs="verbatim,quotes"]
@@ -987,18 +992,25 @@ broadcasting to other connected clients):
987
992
988
993
@Override
989
994
public void registerStompEndpoints(StompEndpointRegistry registry) {
990
- registry.addEndpoint("/portfolio").withSockJS();
995
+ registry.addEndpoint("/portfolio").withSockJS(); // <1>
991
996
}
992
997
993
998
@Override
994
999
public void configureMessageBroker(MessageBrokerRegistry config) {
995
- config.setApplicationDestinationPrefixes("/app");
996
- config.enableSimpleBroker("/topic", "/queue");
1000
+ config.setApplicationDestinationPrefixes("/app"); // <2>
1001
+ config.enableSimpleBroker("/topic", "/queue"); // <3>
997
1002
}
998
1003
}
999
1004
----
1000
1005
1001
- and in XML:
1006
+ <1> `"/portfolio"` is the HTTP URL for the endpoint to which a WebSocket (or SockJS)
1007
+ client will need to connect to for the WebSocket handshake.
1008
+ <2> STOMP messages whose destination header begins with `"/app"` are routed to
1009
+ `@MessageMapping` methods in `@Controller` classes.
1010
+ <3> Use the built-in, message broker for subscriptions and broadcasting;
1011
+ Route messages whose destination header begins with "/topic" or "/queue" to the broker.
1012
+
1013
+ The same configuration in XML:
1002
1014
1003
1015
[source,xml,indent=0]
1004
1016
[subs="verbatim,quotes,attributes"]
@@ -1024,18 +1036,11 @@ and in XML:
1024
1036
1025
1037
[NOTE]
1026
1038
====
1027
- The "/app" prefix is arbitrary. You can pick any prefix. It's simply meant to differentiate
1028
- messages to be routed to message-handling methods to do application work vs messages
1029
- to be routed to the broker to broadcast to subscribed clients.
1030
-
1031
- The "/topic" and "/queue" prefixes depend on the broker in use. In the case of the simple,
1032
- in-memory broker the prefixes do not have any special meaning; it's merely a convention
1033
- that indicates how the destination is used (pub-sub targetting many subscribers or
1034
- point-to-point messages typically targeting an individual recipient).
1035
- In the case of using a dedicated broker, most brokers use "/topic" as
1036
- a prefix for destinations with pub-sub semantics and "/queue" for destinations
1037
- with point-to-point semantics. Check the STOMP page of the broker to see the destination
1038
- semantics it supports.
1039
+ For the built-in, simple broker the "/topic" and "/queue" prefixes do not have any special
1040
+ meaning. They're merely a convention to differentiate between pub-sub vs point-to-point
1041
+ messaging (i.e. many subscribers vs one consumer). When using an external broker, please
1042
+ check the STOMP page of the broker to understand what kind of STOMP destinations and
1043
+ prefixes it supports.
1039
1044
====
1040
1045
1041
1046
@@ -1075,60 +1080,64 @@ sections <<websocket-stomp-handle-broker-relay-configure>> and
1075
1080
[[websocket-stomp-message-flow]]
1076
1081
=== Flow of Messages
1077
1082
1078
- When a STOMP endpoint is configured, the Spring application acts as the STOMP broker
1079
- to connected clients. This section provides a big picture overview of how messages flow
1080
- within the application.
1083
+ Once a STOMP endpoint is exposed, the Spring application becomes a STOMP broker for
1084
+ connected clients. This section describes the flow of messages on the server side.
1081
1085
1082
- The `spring-messaging` module provides the foundation for asynchronous message processing.
1083
- It contains a number of abstractions that originated in the
1084
- https://spring.io/spring-integration[Spring Integration] project and are intended
1085
- for use as building blocks in messaging applications:
1086
+ The `spring-messaging` module contains foundational support for messaging applications
1087
+ that originated in https://spring.io/spring-integration[Spring Integration] and was
1088
+ later extracted and incorporated into the Spring Framework for broader use across many
1089
+ https://spring.io/projects[Spring projects] and application scenarios.
1090
+ Below is a list of a few of the available messaging abstractions:
1086
1091
1087
1092
* {api-spring-framework}/messaging/Message.html[Message] --
1088
- a message with headers and a payload.
1093
+ simple representation for a message including headers and payload.
1089
1094
* {api-spring-framework}/messaging/MessageHandler.html[MessageHandler] --
1090
- a contract for handling a message.
1095
+ contract for handling a message.
1091
1096
* {api-spring-framework}/messaging/MessageChannel.html[MessageChannel] --
1092
- a contract for sending a message enabling loose coupling between senders and receivers .
1097
+ contract for sending a message that enables loose coupling between producers and consumers .
1093
1098
* {api-spring-framework}/messaging/SubscribableChannel.html[SubscribableChannel] --
1094
- extends `MessageChannel` and sends messages to registered `MessageHandler` subscribers.
1099
+ `MessageChannel` with `MessageHandler` subscribers.
1095
1100
* {api-spring-framework}/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel] --
1096
- a concrete implementation of `SubscribableChannel` that can deliver messages
1097
- asynchronously via a thread pool.
1101
+ `SubscribableChannel` that uses an `Executor` for delivering messages.
1098
1102
1099
- The `@EnableWebSocketMessageBroker` Java config and the `<websocket:message-broker>` XML config
1100
- both assemble a concrete message flow. Below is a diagram of the part of the setup when using
1101
- the simple, in-memory broker:
1103
+ Both the Java config (i.e. `@EnableWebSocketMessageBroker`) and the XML namespace config
1104
+ (i.e. `<websocket:message-broker>`) use the above components to assemble a message
1105
+ workflow. The diagram below shows the components used when the simple, built-in message
1106
+ broker is enabled:
1102
1107
1103
1108
image::images/message-flow-simple-broker.png[]
1104
1109
1105
- The above setup that includes 3 message channels:
1110
+ There are 3 message channels in the above diagram :
1106
1111
1107
- * `"clientInboundChannel"` for messages from WebSocket clients.
1108
- * `"clientOutboundChannel"` for messages to WebSocket clients.
1109
- * `"brokerChannel"` for messages to the broker from within the application.
1112
+ * `"clientInboundChannel"` -- for passing messages received from WebSocket clients.
1113
+ * `"clientOutboundChannel"` -- for sending server messages to WebSocket clients.
1114
+ * `"brokerChannel"` -- for sending messages to the message broker from within
1115
+ server-side, application code.
1110
1116
1111
- The same three channels are also used with a dedicated broker except here a
1112
- "broker relay" takes the place of the simple broker :
1117
+ The next diagram shows the components used when an external broker (e.g. RabbitMQ)
1118
+ is configured for managing subscriptions and broadcasting messages :
1113
1119
1114
1120
image::images/message-flow-broker-relay.png[]
1115
1121
1116
- Messages on the `"clientInboundChannel"` can flow to annotated
1117
- methods for application handling (e.g. a stock trade execution request) or can
1118
- be forwarded to the broker (e.g. client subscribing for stock quotes).
1119
- The STOMP destination is used for simple prefix-based routing. For example
1120
- the "/app" prefix could route messages to annotated methods while the "/topic"
1121
- and "/queue" prefixes could route messages to the broker.
1122
+ The main difference in the above diagram is the use of the "broker relay" for passing
1123
+ messages up to the external STOMP broker over TCP, and for passing messages down from the
1124
+ broker to subscribed clients.
1125
+
1126
+ When messages are received from a WebSocket connectin, they're decoded to STOMP frames,
1127
+ then turned into a Spring `Message` representation, and sent to the
1128
+ `"clientInboundChannel"` for further processing. For example STOMP messages whose
1129
+ destination header starts with `"/app"` may be routed to `@MessageMapping` methods in
1130
+ annotated controllers, while `"/topic"` and `"/queue"` messages may be routed directly
1131
+ to the message broker.
1122
1132
1123
- When a message-handling annotated method has a return type, its return
1124
- value is sent as the payload of a Spring `Message` to the `"brokerChannel"`.
1125
- The broker in turn broadcasts the message to clients. Sending a message
1126
- to a destination can also be done from anywhere in the application with
1127
- the help of a messaging template. For example, an HTTP POST handling method
1128
- can broadcast a message to connected clients, or a service component may
1129
- periodically broadcast stock quotes.
1133
+ An annotated `@Controller` handling a STOMP message from a client may send a message to
1134
+ the message broker through the `"brokerChannel"`, and the broker will broadcast the
1135
+ message to matching subscribers through the `"clientOutboundChannel"`. The same
1136
+ controller can also do the same in response to HTTP requests, so a client may perform an
1137
+ HTTP POST and then an `@PostMapping` method can send a message to the message broker
1138
+ to broadcast to subscribed clients.
1130
1139
1131
- Below is a simple example to illustrate the flow of messages :
1140
+ Let's trace the flow through a simple example. Given the following server setup :
1132
1141
1133
1142
[source,java,indent=0]
1134
1143
[subs="verbatim,quotes"]
@@ -1162,18 +1171,22 @@ Below is a simple example to illustrate the flow of messages:
1162
1171
1163
1172
----
1164
1173
1165
- The following explains the message flow for the above example:
1166
-
1167
- * WebSocket clients connect to the WebSocket endpoint at "/portfolio".
1168
- * Subscriptions to "/topic/greeting" pass through the "clientInboundChannel"
1169
- and are forwarded to the broker.
1170
- * Greetings sent to "/app/greeting" pass through the "clientInboundChannel"
1171
- and are forwarded to the `GreetingController`. The controller adds the current
1172
- time, and the return value is passed through the "brokerChannel" as a message
1173
- to "/topic/greeting" (destination is selected based on a convention but can be
1174
- overridden via `@SendTo`).
1175
- * The broker in turn broadcasts messages to subscribers, and they pass through
1176
- the `"clientOutboundChannel"`.
1174
+ . Client connects to `"http://localhost:8080/portfolio"` and once a WebSocket connection
1175
+ is established, STOMP frames begin to flow on it.
1176
+ . Client sends SUBSCRIBE frame with destination header `"/topic/greeting"`. Once received
1177
+ and decoded, the message is sent to the `"clientInboundChannel"`, then routed to the
1178
+ message broker which stores the client subscription.
1179
+ . Client sends SEND frame to `"/app/greeting"`. The `"/app"` prefix helps to route it to
1180
+ annotated controllers. After the `"/app"` prefix is stripped, the remaining `"/greeting"`
1181
+ part of the destination is mapped to the `@MessageMapping` method in `GreetingController`.
1182
+ . The value returned from `GreetingController` is turned into a Spring `Message` with
1183
+ a payload based on the return value and a default destination header of
1184
+ `"/topic/greeting"` (derived from the input destination with `"/app"` replaced by
1185
+ `"/topic"`). The resulting message is sent to the "brokerChannel" and handled
1186
+ by the message broker.
1187
+ . The message broker finds all matching subscribers, and sends a MESSAGE frame to each
1188
+ through the `"clientOutboundChannel"` from where messages are encoded as STOMP frames
1189
+ and sent on the WebSocket connection.
1177
1190
1178
1191
The next section provides more details on annotated methods including the
1179
1192
kinds of arguments and return values supported.
0 commit comments