Skip to content

Commit fd17518

Browse files
committed
Revise section explaining the flow of STOMP messages
1 parent 84133c8 commit fd17518

File tree

1 file changed

+95
-131
lines changed

1 file changed

+95
-131
lines changed

src/asciidoc/index.adoc

Lines changed: 95 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -944,7 +944,7 @@ to use in applications along with an annotation programming model for routing an
944944
processing STOMP messages from WebSocket clients. As a result an `@Controller`
945945
can now contain both `@RequestMapping` and `@MessageMapping` methods for handling
946946
HTTP requests and messages from WebSocket-connected clients. The new `spring-messaging`
947-
module also contains key abstractions from the
947+
module also contains key abstractions formerly from the
948948
http://projects.spring.io/spring-integration/[Spring Integration] project such as
949949
`Message`, `MessageChannel`, `MessageHandler`, and others to serve as a foundation
950950
for messaging-based applications.
@@ -36685,7 +36685,7 @@ and also provides additional value-add as explained in the rest of the introduct
3668536685

3668636686

3668736687
[[websocket-into-fallback-options]]
36688-
==== Fallback Options
36688+
==== WebSocket Fallback Options
3668936689
An important challenge to adoption is the lack of support for WebSocket in some
3669036690
browsers. Notably the first Internet Explorer version to support WebSocket is
3669136691
version 10 (see http://caniuse.com/websockets for support by browser versions).
@@ -36707,7 +36707,7 @@ modifying the application otherwise.
3670736707

3670836708

3670936709
[[websocket-intro-architecture]]
36710-
==== Messaging Architecture
36710+
==== A Messaging Architecture
3671136711
Aside from short-to-midterm adoption challenges, using WebSocket
3671236712
brings up important design considerations that are important to recognize
3671336713
early on, especially in contrast to what we know about building web applications today.
@@ -36734,7 +36734,7 @@ annotation based programming model.
3673436734

3673536735

3673636736
[[websocket-intro-sub-protocol]]
36737-
==== Sub-Protocol Support
36737+
==== Sub-Protocol Support in WebSocket
3673836738
WebSocket does imply a __messaging architecture__ but does not mandate the
3673936739
use of any specific __messaging protocol__. It is a very thin layer over TCP
3674036740
that transforms a stream of bytes into a stream of messages
@@ -36767,7 +36767,7 @@ WebSocket and over the web.
3676736767

3676836768

3676936769
[[websocket-intro-when-to-use]]
36770-
==== When To Use WebSocket?
36770+
==== Should I Use WebSocket?
3677136771
With all the design considerations surrounding the use of WebSocket, it is
3677236772
reasonable to ask when is it appropriate to use?
3677336773

@@ -36810,7 +36810,7 @@ WebSocket clients or to a specific user.
3681036810

3681136811

3681236812
[[websocket-server]]
36813-
=== WebSocket Server
36813+
=== WebSocket API
3681436814
The Spring Framework provides a WebSocket API designed to adapt to various WebSocket engines.
3681536815
For example, it runs on JSR-356 runtimes such as Tomcat (7.0.47+), GlassFish (4.0+) and
3681636816
WildFly (8.0+) but can also adapt to other WebSocket runtimes such as the Jetty (9.1+)
@@ -37448,7 +37448,7 @@ log category to TRACE.
3744837448
====
3744937449

3745037450
[[websocket-fallback-cors]]
37451-
==== CORS Headers for SockJS Requests
37451+
==== CORS Headers for SockJS
3745237452

3745337453
The SockJS protocol uses CORS for cross-domain support in the XHR streaming and
3745437454
polling transports. Therefore CORS headers are added automatically unless the
@@ -37472,7 +37472,7 @@ SockJS endpoint prefix thus letting Spring's SockJsService handle it.
3747237472

3747337473

3747437474
[[websocket-stomp]]
37475-
=== STOMP Messaging
37475+
=== STOMP Over WebSocket Messaging Architecture
3747637476
The WebSocket protocol defines two main types of messages -- text and binary --
3747737477
but leaves their content undefined. Instead it's expected that client and
3747837478
server may agree on using a sub-protocol, i.e. a higher-level protocol that defines
@@ -37575,7 +37575,7 @@ Spring MVC provides a programming model based on HTTP.
3757537575

3757637576

3757737577
[[websocket-stomp-enable]]
37578-
==== Enable STOMP (over WebSocket)
37578+
==== Enable STOMP over WebSocket
3757937579
The Spring Framework provides support for using STOMP over WebSocket through
3758037580
the +spring-messaging+ and +spring-websocket+ modules. It's easy to enable it.
3758137581

@@ -37666,56 +37666,65 @@ sections <<websocket-stomp-handle-broker-relay-configure>> and
3766637666

3766737667

3766837668
[[websocket-stomp-handle]]
37669-
==== Overview of STOMP Message Handling
37670-
37671-
When a STOMP endpoint is configured, the Spring application effectively becomes
37672-
the broker to connected clients, handling incoming messages and broadcasting
37673-
messages back to them. This part of the documentation describes how STOMP
37674-
messages are handled within the application.
37675-
37676-
As mentioned in the <<websocket-intro-architecture,introduction>> the
37677-
`spring-messaging` module contains key abstractions from the
37678-
https://spring.io/spring-integration[Spring Integration] project, including
37679-
`Message`, `MessageChannel`, `MessageHandler` and a few others.
37680-
37681-
[NOTE]
37682-
====
37683-
Spring Integration 4 will be the first version to start using the abstractions
37684-
from the package structure of the `spring-messaging` module as opposed to its
37685-
own present packages. Spring Integration also provides many additional
37686-
abstractions and implementations in support of the well-known
37687-
EAI patterns (http://www.eaipatterns.com/[enterprise integration patterns]).
37688-
====
37689-
37690-
{javadoc-baseurl}/org/springframework/messaging/MessageChannel.html[MessageChannel]
37691-
is a simple contract for passing messages between components without
37692-
creating tight coupling among them.
37693-
{javadoc-baseurl}/org/springframework/messaging/SubscribableChannel.html[SubscribableChannel] extends
37694-
it, with the ability to register subscribers; and
37695-
{javadoc-baseurl}/org/springframework/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel]
37696-
is an implementation that passes messages to subscribers in
37697-
the same thread or a different thread depending on whether it has been provided with
37698-
a `java.util.concurrent.Executor`. This enables assembling message
37699-
handling flows from various components and modifying them through configuration.
37700-
37701-
The provided Java-config `@EnableWebSocketMessageBroker` and XML namespace
37702-
`<websocket:message-broker>` each put together a default message handling
37703-
flow for applications to use, as explained next. This flow can be modified,
37704-
customized, or extended. For example, an application can add a
37705-
{javadoc-baseurl}/org/springframework/messaging/support/ChannelInterceptor.html[ChannelInterceptor]
37706-
to any message channel in order to intercept messages passing through it;
37707-
it can register additional message handling components, alternate between
37708-
synchronous and asynchronous message passing, and so on.
37709-
37710-
Incoming client STOMP messages are passed to a message channel with the name
37711-
`"clientInboundChannel"`. By default the messages are routed to annotated
37712-
methods as well as to a "simple" message broker. This simple message broker
37713-
automatically records subscriptions, in-memory, and broadcasts messages as
37714-
necessary. As explained later, you can also use a full-featured message broker
37715-
(e.g. RabbitMQ, ActiveMQ, and any other broker that supports STOMP) to manage
37716-
subscriptions and broadcast messages.
37717-
37718-
Below is example configuration:
37669+
==== Flow of Messages
37670+
37671+
When a STOMP endpoint is configured, the Spring application becomes the broker to
37672+
connected clients. It handles incoming messages and broadcasts messages back.
37673+
This section provides the big picture of how messages are handled and how
37674+
they flow inside the application.
37675+
37676+
The `spring-messaging` module contains a number of abstractions for use in
37677+
messaging applications which originated in the
37678+
https://spring.io/spring-integration[Spring Integration] project and
37679+
have been in use for a long time but are now part of the Spring Framework.
37680+
Listed below are some of the main building blocks:
37681+
37682+
* {javadoc-baseurl}/org/springframework/messaging/Message.html[Message]
37683+
represents a message with headers and a payload.
37684+
* {javadoc-baseurl}/org/springframework/messaging/MessageHandler.html[MessageHandler]
37685+
a simple contract components that handle a message.
37686+
* {javadoc-baseurl}/org/springframework/messaging/MessageChannel.html[MessageChannel]
37687+
a simple contract for sending a message that enables loose coupling between
37688+
senders and receivers.
37689+
* {javadoc-baseurl}/org/springframework/messaging/SubscribableChannel.html[SubscribableChannel]
37690+
extends `MessageChannel` and sends messages to registered `MessageHandler` subscribers.
37691+
* {javadoc-baseurl}/org/springframework/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel]
37692+
a concrete implementation of `SubscribableChannel` that can deliver messages
37693+
asynchronously using threads from a thread pool.
37694+
37695+
The above building blocks can be used to construct a message flow.
37696+
37697+
The provided configuration for STOMP over WebSocket, both Java config and XML namespace,
37698+
assemble just such a concrete message flow that consists of the following 3
37699+
subscribable channels:
37700+
37701+
* `"clientInboundChannel"` for messages from WebSocket clients; every incoming
37702+
WebSocket message that is decoded and turned into a Spring Message representing
37703+
a STOMP frame is passed through this channel.
37704+
* `"clientOutboundChannel"` for messages to WebSocket clients; every outgoing
37705+
Spring Message representing a STOMP frame that is encoded and sent as a
37706+
WebSocket message is passed through this channel.
37707+
* `"brokerChannel"` for messages to the broker from within the application and
37708+
ultimately intended to reach connected clients; every message that the application
37709+
wants to send to clients passes through this channel.
37710+
37711+
Messages through `"clientInboundChannel"` can flow to annotated
37712+
methods for application handling (e.g. a stock trade execution request) or can
37713+
be forwarded to the broker (e.g. client subscribing for stock quotes).
37714+
The STOMP destination is used for simple prefix-based routing. For example
37715+
the "/app" prefix routes messages annotated methods while the "/topic" or
37716+
"/queue" prefix route messages to the broker.
37717+
37718+
When a message-handling annotated method has a return type, its return
37719+
value is sent as the payload of a Spring Message to the `"brokerChannel"`.
37720+
The broker in turn broadcasts the message to clients. Sending an object as
37721+
the payload of a Message to a specific destination can also easily be done
37722+
from anywhere in the application with the help of a messaging template.
37723+
For example a an HTTP POST handling method can broadcast a message to
37724+
connected clients or a service component may periodically broadcast
37725+
stock quotes.
37726+
37727+
Below is a simple example to illustrate the flow of messages:
3771937728

3772037729
[source,java,indent=0]
3772137730
[subs="verbatim,quotes"]
@@ -37726,96 +37735,49 @@ Below is example configuration:
3772637735

3772737736
@Override
3772837737
public void registerStompEndpoints(StompEndpointRegistry registry) {
37729-
registry.addEndpoint("/portfolio").withSockJS();
37738+
registry.addEndpoint("/portfolio");
3773037739
}
3773137740

3773237741
@Override
3773337742
public void configureMessageBroker(MessageBrokerRegistry registry) {
37734-
registry.enableSimpleBroker("/topic/");
3773537743
registry.setApplicationDestinationPrefixes("/app");
37744+
registry.enableSimpleBroker("/topic/");
3773637745
}
3773737746

3773837747
}
37739-
----
37740-
37741-
XML configuration equivalent:
3774237748

37743-
[source,xml,indent=0]
37744-
[subs="verbatim,quotes,attributes"]
37745-
----
37746-
<beans xmlns="http://www.springframework.org/schema/beans"
37747-
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
37748-
xmlns:websocket="http://www.springframework.org/schema/websocket"
37749-
xsi:schemaLocation="
37750-
http://www.springframework.org/schema/beans
37751-
http://www.springframework.org/schema/beans/spring-beans.xsd
37752-
http://www.springframework.org/schema/websocket
37753-
http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd">
37754-
37755-
<websocket:message-broker application-destination-prefix="/app">
37756-
<websocket:stomp-endpoint path="/portfolio" />
37757-
<websocket:sockjs/>
37758-
</websocket:stomp-endpoint>
37759-
<websocket:simple-broker prefix="/topic"/>
37760-
</websocket:message-broker>
37761-
37762-
</beans>
37763-
----
37764-
37765-
The configuration example assigns destination prefixes -- `/app` for filtering
37766-
messages to annotated methods and `/topic` for messages to the broker. The
37767-
examples below demonstrate how this can be used.
37768-
37769-
The destination prefix should not be included in annotation mappings. For
37770-
example, this method handles messages to destination `/app/greetings`:
37771-
37772-
[source,java,indent=0]
37773-
[subs="verbatim,quotes"]
37774-
----
3777537749
@Controller
3777637750
public class GreetingController {
3777737751

37778-
@MessageMapping("/greetings") {
37779-
public void handle(String greeting) {
37780-
// ...
37752+
@MessageMapping("/greeting") {
37753+
public String handle(String greeting) {
37754+
return "[" + getTimestamp() + ": " + greeting;
3778137755
}
3778237756

3778337757
}
3778437758

3778537759
----
3778637760

37787-
The method accepts a String extracted from the payload of the message,
37788-
possibly converted based on its content type. The method can also return a
37789-
value, which is wrapped as the payload of a new message and sent to a message
37790-
channel named `"brokerChannel"` (which is used for sending messages to the broker
37791-
from within the application). The new message is sent to the same destination
37792-
as that of the client message, but with the default prefix `/topic`
37793-
(you can also use `@SendTo` for any other target destination):
37761+
The following explains how messages flow given the above:
3779437762

37795-
[source,java,indent=0]
37796-
[subs="verbatim,quotes"]
37797-
----
37798-
@Controller
37799-
public class GreetingController {
37800-
37801-
@MessageMapping("/greetings") {
37802-
public String handle(String greeting) {
37803-
return "[" + getTimestamp() + ": " + greeting;
37804-
}
37763+
* WebSocket clients connect to the WebSocket endpoint at "/portfolio".
37764+
* Subscriptions to "/topic/greeting" pass through the "clientInboundChannel"
37765+
and are forwarded to the broker.
37766+
* Greetings sent to "/app/greeting" pass through the "clientInboundChannel"
37767+
and are forwarded to the `GreetingController`. The controller adds the current
37768+
time and the return value is passed through the "brokerChannel" as message
37769+
to "/topic/greeting" (destination is selected based on a convention but can be
37770+
overridden via `@SendTo`).
37771+
* The broker in turn broadcasts messages to subscribers and they pass through
37772+
the `"clientOutboundChannel"`.
3780537773

37806-
}
37774+
The next section provides more details on annotated methods including the
37775+
kinds of arguments and return values supported.
3780737776

37808-
----
37809-
37810-
As a result, to put it all together, a client sends a greeting message to
37811-
destination `/app/greetings`. The message is routed to `GreetingController`,
37812-
which enriches the greeting with a timestamp and sends a new message to the
37813-
broker with destination `/topic/greetings`. The broker then broadcasts the
37814-
message to all subscribed, connected clients.
3781537777

3781637778

3781737779
[[websocket-stomp-handle-annotations]]
37818-
===== Annotation-based Message Handling
37780+
==== Annotation Message Handling
3781937781

3782037782
The `@MessageMapping` annotation is supported on methods of `@Controller`
3782137783
as well as on `@RestController`-annotated classes.
@@ -37873,7 +37835,7 @@ the specified target destination.
3787337835

3787437836

3787537837
[[websocket-stomp-handle-send]]
37876-
===== Sending Messages From Anywhere
37838+
==== Sending Messages
3787737839

3787837840
What if you wanted to send messages to connected clients from any part of the
3787937841
application? Any application component can send messages to the `"brokerChannel"`.
@@ -37908,7 +37870,7 @@ bean of the same type exists.
3790837870

3790937871

3791037872
[[websocket-stomp-handle-simple-broker]]
37911-
===== Simple Message Broker
37873+
==== Simple Broker
3791237874

3791337875
The built-in, simple, message broker handles subscription requests from clients,
3791437876
stores them in memory, and broadcasts messages to connected clients with matching
@@ -37917,7 +37879,7 @@ to Ant-style destination patterns.
3791737879

3791837880

3791937881
[[websocket-stomp-handle-broker-relay]]
37920-
===== Using a Full-Featured Message Broker
37882+
==== Full-Featured Broker
3792137883

3792237884
The simple broker is great for getting started but supports only a subset of
3792337885
STOMP commands (e.g. no acks, receipts, etc), relies on a simple message
@@ -37998,7 +37960,7 @@ subscribed WebSocket clients.
3799837960
In effect, the broker relay enables robust and scalable message broadcasting.
3799937961

3800037962
[[websocket-stomp-handle-broker-relay-configure]]
38001-
===== Configuring Connections To The Full-Featured Broker
37963+
==== Connections To Full-Featured Broker
3800237964

3800337965
A STOMP broker relay maintains a single "system" TCP connection to the broker.
3800437966
This connection is used for messages originating from the server-side application
@@ -38043,7 +38005,7 @@ the TCP connection is established is different from the host providing the
3804338005
cloud-based STOMP service.
3804438006

3804538007
[[websocket-stomp-handle-user]]
38046-
===== Authentication and Handling Messages to User Destinations
38008+
==== Authentication and User Destinations
3804738009

3804838010
An application can also send messages targeting a specific user.
3804938011

@@ -38083,8 +38045,10 @@ So in that case the client could subscribe to `/user/exchange/amq.direct/positio
3808338045
ActiveMQ has http://activemq.apache.org/delete-inactive-destinations.html[configuration options]
3808438046
for purging inactive destinations.
3808538047

38048+
38049+
3808638050
[[websocket-stomp-testing]]
38087-
===== Testing Message Handling Controllers
38051+
==== Testing Annotated Controller Methods
3808838052

3808938053
There are two main approaches to testing applications using Spring's STOMP over
3809038054
WebSocket support. The first is to write server-side tests verifying the functionality

0 commit comments

Comments
 (0)