Browse Source

Revise section explaining the flow of STOMP messages

pull/499/head
Rossen Stoyanchev 12 years ago
parent
commit
fd175180b9
  1. 226
      src/asciidoc/index.adoc

226
src/asciidoc/index.adoc

@ -944,7 +944,7 @@ to use in applications along with an annotation programming model for routing an @@ -944,7 +944,7 @@ to use in applications along with an annotation programming model for routing an
processing STOMP messages from WebSocket clients. As a result an `@Controller`
can now contain both `@RequestMapping` and `@MessageMapping` methods for handling
HTTP requests and messages from WebSocket-connected clients. The new `spring-messaging`
module also contains key abstractions from the
module also contains key abstractions formerly from the
http://projects.spring.io/spring-integration/[Spring Integration] project such as
`Message`, `MessageChannel`, `MessageHandler`, and others to serve as a foundation
for messaging-based applications.
@ -36685,7 +36685,7 @@ and also provides additional value-add as explained in the rest of the introduct @@ -36685,7 +36685,7 @@ and also provides additional value-add as explained in the rest of the introduct
[[websocket-into-fallback-options]]
==== Fallback Options
==== WebSocket Fallback Options
An important challenge to adoption is the lack of support for WebSocket in some
browsers. Notably the first Internet Explorer version to support WebSocket is
version 10 (see http://caniuse.com/websockets for support by browser versions).
@ -36707,7 +36707,7 @@ modifying the application otherwise. @@ -36707,7 +36707,7 @@ modifying the application otherwise.
[[websocket-intro-architecture]]
==== Messaging Architecture
==== A Messaging Architecture
Aside from short-to-midterm adoption challenges, using WebSocket
brings up important design considerations that are important to recognize
early on, especially in contrast to what we know about building web applications today.
@ -36734,7 +36734,7 @@ annotation based programming model. @@ -36734,7 +36734,7 @@ annotation based programming model.
[[websocket-intro-sub-protocol]]
==== Sub-Protocol Support
==== Sub-Protocol Support in WebSocket
WebSocket does imply a __messaging architecture__ but does not mandate the
use of any specific __messaging protocol__. It is a very thin layer over TCP
that transforms a stream of bytes into a stream of messages
@ -36767,7 +36767,7 @@ WebSocket and over the web. @@ -36767,7 +36767,7 @@ WebSocket and over the web.
[[websocket-intro-when-to-use]]
==== When To Use WebSocket?
==== Should I Use WebSocket?
With all the design considerations surrounding the use of WebSocket, it is
reasonable to ask when is it appropriate to use?
@ -36810,7 +36810,7 @@ WebSocket clients or to a specific user. @@ -36810,7 +36810,7 @@ WebSocket clients or to a specific user.
[[websocket-server]]
=== WebSocket Server
=== WebSocket API
The Spring Framework provides a WebSocket API designed to adapt to various WebSocket engines.
For example, it runs on JSR-356 runtimes such as Tomcat (7.0.47+), GlassFish (4.0+) and
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. @@ -37448,7 +37448,7 @@ log category to TRACE.
====
[[websocket-fallback-cors]]
==== CORS Headers for SockJS Requests
==== CORS Headers for SockJS
The SockJS protocol uses CORS for cross-domain support in the XHR streaming and
polling transports. Therefore CORS headers are added automatically unless the
@ -37472,7 +37472,7 @@ SockJS endpoint prefix thus letting Spring's SockJsService handle it. @@ -37472,7 +37472,7 @@ SockJS endpoint prefix thus letting Spring's SockJsService handle it.
[[websocket-stomp]]
=== STOMP Messaging
=== 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 client and
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. @@ -37575,7 +37575,7 @@ Spring MVC provides a programming model based on HTTP.
[[websocket-stomp-enable]]
==== Enable STOMP (over WebSocket)
==== 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.
@ -37666,56 +37666,65 @@ sections <<websocket-stomp-handle-broker-relay-configure>> and @@ -37666,56 +37666,65 @@ sections <<websocket-stomp-handle-broker-relay-configure>> and
[[websocket-stomp-handle]]
==== Overview of STOMP Message Handling
When a STOMP endpoint is configured, the Spring application effectively becomes
the broker to connected clients, handling incoming messages and broadcasting
messages back to them. This part of the documentation describes how STOMP
messages are handled within the application.
As mentioned in the <<websocket-intro-architecture,introduction>> the
`spring-messaging` module contains key abstractions from the
https://spring.io/spring-integration[Spring Integration] project, including
`Message`, `MessageChannel`, `MessageHandler` and a few others.
[NOTE]
====
Spring Integration 4 will be the first version to start using the abstractions
from the package structure of the `spring-messaging` module as opposed to its
own present packages. Spring Integration also provides many additional
abstractions and implementations in support of the well-known
EAI patterns (http://www.eaipatterns.com/[enterprise integration patterns]).
====
{javadoc-baseurl}/org/springframework/messaging/MessageChannel.html[MessageChannel]
is a simple contract for passing messages between components without
creating tight coupling among them.
{javadoc-baseurl}/org/springframework/messaging/SubscribableChannel.html[SubscribableChannel] extends
it, with the ability to register subscribers; and
{javadoc-baseurl}/org/springframework/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel]
is an implementation that passes messages to subscribers in
the same thread or a different thread depending on whether it has been provided with
a `java.util.concurrent.Executor`. This enables assembling message
handling flows from various components and modifying them through configuration.
The provided Java-config `@EnableWebSocketMessageBroker` and XML namespace
`<websocket:message-broker>` each put together a default message handling
flow for applications to use, as explained next. This flow can be modified,
customized, or extended. For example, an application can add a
{javadoc-baseurl}/org/springframework/messaging/support/ChannelInterceptor.html[ChannelInterceptor]
to any message channel in order to intercept messages passing through it;
it can register additional message handling components, alternate between
synchronous and asynchronous message passing, and so on.
Incoming client STOMP messages are passed to a message channel with the name
`"clientInboundChannel"`. By default the messages are routed to annotated
methods as well as to a "simple" message broker. This simple message broker
automatically records subscriptions, in-memory, and broadcasts messages as
necessary. As explained later, you can also use a full-featured message broker
(e.g. RabbitMQ, ActiveMQ, and any other broker that supports STOMP) to manage
subscriptions and broadcast messages.
Below is example configuration:
==== Flow of Messages
When a STOMP endpoint is configured, the Spring application becomes the broker to
connected clients. It handles incoming messages and broadcasts messages back.
This section provides the big picture of how messages are handled and how
they flow inside the application.
The `spring-messaging` module contains a number of abstractions for use in
messaging applications which originated in the
https://spring.io/spring-integration[Spring Integration] project and
have been in use for a long time but are now part of the Spring Framework.
Listed below are some of the main building blocks:
* {javadoc-baseurl}/org/springframework/messaging/Message.html[Message]
represents a message with headers and a payload.
* {javadoc-baseurl}/org/springframework/messaging/MessageHandler.html[MessageHandler]
a simple contract components that handle a message.
* {javadoc-baseurl}/org/springframework/messaging/MessageChannel.html[MessageChannel]
a simple contract for sending a message that enables loose coupling between
senders and receivers.
* {javadoc-baseurl}/org/springframework/messaging/SubscribableChannel.html[SubscribableChannel]
extends `MessageChannel` and sends messages to registered `MessageHandler` subscribers.
* {javadoc-baseurl}/org/springframework/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel]
a concrete implementation of `SubscribableChannel` that can deliver messages
asynchronously using threads from a thread pool.
The above building blocks can be used to construct a message flow.
The provided configuration for STOMP over WebSocket, both Java config and XML namespace,
assemble just such a concrete message flow that consists of the following 3
subscribable channels:
* `"clientInboundChannel"` for messages from WebSocket clients; every incoming
WebSocket message that is decoded and turned into a Spring Message representing
a STOMP frame is passed through this channel.
* `"clientOutboundChannel"` for messages to WebSocket clients; every outgoing
Spring Message representing a STOMP frame that is encoded and sent as a
WebSocket message is passed through this channel.
* `"brokerChannel"` for messages to the broker from within the application and
ultimately intended to reach connected clients; every message that the application
wants to send to clients passes through this channel.
Messages through `"clientInboundChannel"` can flow to annotated
methods for application handling (e.g. a stock trade execution request) or can
be forwarded to the broker (e.g. client subscribing for stock quotes).
The STOMP destination is used for simple prefix-based routing. For example
the "/app" prefix routes messages annotated methods while the "/topic" or
"/queue" prefix route messages to the broker.
When a message-handling annotated method has a return type, its return
value is sent as the payload of a Spring Message to the `"brokerChannel"`.
The broker in turn broadcasts the message to clients. Sending an object as
the payload of a Message to a specific destination can also easily be done
from anywhere in the application with the help of a messaging template.
For example a an HTTP POST handling method can broadcast a message to
connected clients or a service component may periodically broadcast
stock quotes.
Below is a simple example to illustrate the flow of messages:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -37726,96 +37735,49 @@ Below is example configuration: @@ -37726,96 +37735,49 @@ Below is example configuration:
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").withSockJS();
registry.addEndpoint("/portfolio");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic/");
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic/");
}
}
----
XML configuration equivalent:
[source,xml,indent=0]
[subs="verbatim,quotes,attributes"]
----
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd">
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/portfolio" />
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic"/>
</websocket:message-broker>
</beans>
----
The configuration example assigns destination prefixes -- `/app` for filtering
messages to annotated methods and `/topic` for messages to the broker. The
examples below demonstrate how this can be used.
The destination prefix should not be included in annotation mappings. For
example, this method handles messages to destination `/app/greetings`:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Controller
public class GreetingController {
@MessageMapping("/greetings") {
public void handle(String greeting) {
// ...
@MessageMapping("/greeting") {
public String handle(String greeting) {
return "[" + getTimestamp() + ": " + greeting;
}
}
----
The method accepts a String extracted from the payload of the message,
possibly converted based on its content type. The method can also return a
value, which is wrapped as the payload of a new message and sent to a message
channel named `"brokerChannel"` (which is used for sending messages to the broker
from within the application). The new message is sent to the same destination
as that of the client message, but with the default prefix `/topic`
(you can also use `@SendTo` for any other target destination):
The following explains how messages flow given the above:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Controller
public class GreetingController {
@MessageMapping("/greetings") {
public String handle(String greeting) {
return "[" + getTimestamp() + ": " + greeting;
}
* WebSocket clients connect to the WebSocket endpoint at "/portfolio".
* Subscriptions to "/topic/greeting" pass through the "clientInboundChannel"
and are forwarded to the broker.
* Greetings sent to "/app/greeting" pass through the "clientInboundChannel"
and are forwarded to the `GreetingController`. The controller adds the current
time and the return value is passed through the "brokerChannel" as message
to "/topic/greeting" (destination is selected based on a convention but can be
overridden via `@SendTo`).
* The broker in turn broadcasts messages to subscribers and they pass through
the `"clientOutboundChannel"`.
}
The next section provides more details on annotated methods including the
kinds of arguments and return values supported.
----
As a result, to put it all together, a client sends a greeting message to
destination `/app/greetings`. The message is routed to `GreetingController`,
which enriches the greeting with a timestamp and sends a new message to the
broker with destination `/topic/greetings`. The broker then broadcasts the
message to all subscribed, connected clients.
[[websocket-stomp-handle-annotations]]
===== Annotation-based Message Handling
==== Annotation Message Handling
The `@MessageMapping` annotation is supported on methods of `@Controller`
as well as on `@RestController`-annotated classes.
@ -37873,7 +37835,7 @@ the specified target destination. @@ -37873,7 +37835,7 @@ the specified target destination.
[[websocket-stomp-handle-send]]
===== Sending Messages From Anywhere
==== Sending Messages
What if you wanted to send messages to connected clients from any part of the
application? Any application component can send messages to the `"brokerChannel"`.
@ -37908,7 +37870,7 @@ bean of the same type exists. @@ -37908,7 +37870,7 @@ bean of the same type exists.
[[websocket-stomp-handle-simple-broker]]
===== Simple Message Broker
==== Simple Broker
The built-in, simple, message broker handles subscription requests from clients,
stores them in memory, and broadcasts messages to connected clients with matching
@ -37917,7 +37879,7 @@ to Ant-style destination patterns. @@ -37917,7 +37879,7 @@ to Ant-style destination patterns.
[[websocket-stomp-handle-broker-relay]]
===== Using a Full-Featured Message Broker
==== Full-Featured Broker
The simple broker is great for getting started but supports only a subset of
STOMP commands (e.g. no acks, receipts, etc), relies on a simple message
@ -37998,7 +37960,7 @@ subscribed WebSocket clients. @@ -37998,7 +37960,7 @@ subscribed WebSocket clients.
In effect, the broker relay enables robust and scalable message broadcasting.
[[websocket-stomp-handle-broker-relay-configure]]
===== Configuring Connections To The Full-Featured Broker
==== Connections To Full-Featured Broker
A STOMP broker relay maintains a single "system" TCP connection to the broker.
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 @@ -38043,7 +38005,7 @@ the TCP connection is established is different from the host providing the
cloud-based STOMP service.
[[websocket-stomp-handle-user]]
===== Authentication and Handling Messages to User Destinations
==== Authentication and User Destinations
An application can also send messages targeting a specific user.
@ -38083,8 +38045,10 @@ So in that case the client could subscribe to `/user/exchange/amq.direct/positio @@ -38083,8 +38045,10 @@ So in that case the client could subscribe to `/user/exchange/amq.direct/positio
ActiveMQ has http://activemq.apache.org/delete-inactive-destinations.html[configuration options]
for purging inactive destinations.
[[websocket-stomp-testing]]
===== Testing Message Handling Controllers
==== Testing Annotated Controller Methods
There are two main approaches to testing applications using Spring's STOMP over
WebSocket support. The first is to write server-side tests verifying the functionality

Loading…
Cancel
Save