Skip to content

Commit e637418

Browse files
committed
Update stomp/websocket documentation
1 parent f368639 commit e637418

File tree

2 files changed

+200
-5
lines changed

2 files changed

+200
-5
lines changed

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/MessageMapping.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@
6161
* WebSocket session on which the message was received. Regular HTTP-based
6262
* authentication (e.g. Spring Security based) can be used to secure the
6363
* HTTP handshake that initiates WebSocket sessions.</li>
64-
* </ul
65-
* >
64+
* </ul>
6665
* <p>By default the return value is wrapped as a message and sent to the destination
6766
* specified with an {@link SendTo} method-level annotation.
6867
*

src/asciidoc/index.adoc

Lines changed: 199 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37058,7 +37058,7 @@ and server both need to understand how to interpret messages.
3705837058

3705937059

3706037060
[[websocket-stomp-overview]]
37061-
==== Overview of the STOMP Protocol
37061+
==== Overview of STOMP
3706237062
http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple
3706337063
messaging protocol originally created to connect to enterprise message brokers from
3706437064
scripting languages such as Ruby, Python and Perl. It is designed to address a
@@ -37345,8 +37345,10 @@ example this method handles messages to destination "/app/greetings":
3734537345
The method accepts a String extracted from the payload of the message,
3734637346
possibly converted based on its content type. The method can also return a
3734737347
value, which is wrapped as the payload of a new message and sent to a message
37348-
channel named `"brokerChannel"` to the same destination as the client message
37349-
but with a new prefix ("/topic" by default). The `@SendTo` annotation :
37348+
channel named `"brokerChannel"` used for sending messages to the broker
37349+
from within the application. The new message is sent to the same destination
37350+
as that of the client message but with the default prefix "/topic"
37351+
(`@SendTo` can be used for any other target destination):
3735037352

3735137353
[source,java,indent=0]
3735237354
[subs="verbatim,quotes"]
@@ -37369,7 +37371,201 @@ which enriches the greeting with a timestamp and sends a new message to the
3736937371
broker with destination "/topic/greetings". The broker then broadcasts the
3737037372
message to all subscribed, connected clients.
3737137373

37374+
[[websocket-stomp-handle-annotations]]
37375+
===== Annotation-based Message Handling
37376+
37377+
The `@MessageMapping` annotation is supported on methods of `@Controller`-annotated classes.
37378+
It can be used for mapping methods to path-like message destinations. It is also
37379+
possible to combine with a type-level `@MessageMapping` for expressing shared
37380+
mappings across all annotated methods within a controller.
37381+
37382+
Destination mappings can contain Ant-style patterns (e.g. "/foo*", "/foo/**")
37383+
and template variables (e.g. "/foo/{id}"), which can then be accessed via
37384+
`@DestinationVariable` method arguments. This should be familiar to Spring MVC
37385+
users, in fact the same `AntPathMatcher` is used for matching destinations based
37386+
on patterns and for extracting template variables.
37387+
37388+
The following method arguments are supported for `@MessageMapping` methods:
37389+
37390+
* `Message` method argument to get access to the complete message being processed.
37391+
* `@Payload`-annotated argument for access to the payload of a message, converted with
37392+
a `org.springframework.messaging.converter.MessageConverter`.
37393+
The presence of the annotation is not required since it is assumed by default.
37394+
* `@Header`-annotated arguments for access to a specific header value along with
37395+
type conversion using an `org.springframework.core.convert.converter.Converter`
37396+
if necessary.
37397+
* `@Headers`-annotated method argument that must also be assignable to `java.util.Map`
37398+
for access to all headers in the message
37399+
* `MessageHeaders` method argument for getting access to a map of all headers
37400+
* `MessageHeaderAccessor`, `SimpMessageHeaderAccessor`, or `StompHeaderAccessor`
37401+
for access to headers via typed accessor methods.
37402+
* `@DestinationVariable`-annotated arguments for access to template
37403+
variables extracted from the message destination. Values will be converted to
37404+
the declared method argument type as necessary.
37405+
* `java.security.Principal` method arguments reflecting the user logged in at
37406+
the time of the WebSocket HTTP handshake.
37407+
37408+
The return value from an `@MessageMapping` method is converted with a
37409+
`org.springframework.messaging.converter.MessageConverter` and used as the body
37410+
of a new message that is then sent, by default, to the `"brokerChannel"` with
37411+
the same destination as the client message but using the prefix "/topic" by
37412+
default. An `@SendTo` message level annotation can be used to specify any
37413+
other destination instead.
37414+
37415+
An `@SubscribeMapping` annotation can also be used to map subscription requests
37416+
to `@Controller` methods. It is supported on the method level but can also be
37417+
combined with a type level `@MessageMapping` annotation that expresses shared
37418+
mappings across all message handling methods within the same controller.
37419+
37420+
By default the return value from an `@SubscribeMapping` method is sent as a
37421+
message directly back to the connected client and does not pass through the
37422+
broker. This useful for implementing request-reply message interactions for
37423+
example to fetch application data when the application UI is being initialized.
37424+
Or alternatively an `@SubscribeMapping` method can be annotated with `@SendTo`
37425+
in which case the resulting message is sent to the `"brokerChannel"` using
37426+
the specified target destination.
37427+
37428+
[[websocket-stomp-handle-send]]
37429+
===== Sending Messages From Anywhere
37430+
37431+
What if you wanted to send messages to connected clients from any part of the
37432+
application? Any application component can send messages to the `"brokerChannel"`.
37433+
The easist way to do that is to have a `SimpMessagingTemplate` injected and
37434+
use it to send messages. Typically it should be easy to have it injected by
37435+
type, for example:
3737237436

37437+
[source,java,indent=0]
37438+
[subs="verbatim,quotes"]
37439+
----
37440+
@Controller
37441+
public class GreetingController {
37442+
37443+
private SimpMessagingTemplate template;
37444+
37445+
@Autowired
37446+
public GreetingController(SimpMessagingTemplate template) {
37447+
this.template = template;
37448+
}
37449+
37450+
@RequestMapping(value="/greetings", method=POST)
37451+
public void greet(String greeting) {
37452+
String text = "[" + getTimestamp() + "]:" + greeting;
37453+
this.template.convertAndSend("/topic/greetings", text);
37454+
}
37455+
37456+
}
37457+
----
37458+
37459+
But it can also be qualified by its name "brokerMessagingTemplate" if another
37460+
bean of the same type exists.
37461+
37462+
[[websocket-stomp-handle-simple-broker]]
37463+
===== Simple Message Broker
37464+
37465+
The built-in, simple, message broker handles subscription requests from clients,
37466+
stores them in memory, and broadcats messages to connected clients with matching
37467+
destinations. The broker supports path-like destinations including subscriptions
37468+
to Ant-style destination patterns.
37469+
37470+
[[websocket-stomp-handle-broker-relay]]
37471+
===== Using a Full-Featured Message Broker
37472+
37473+
The simple broker is great for getting started but supports only a subset of
37474+
STOMP commands (e.g. no acks, receipts, etc), relies on a simple message
37475+
sending loop, and is not suitable for clustering.
37476+
37477+
Instead applications can use a full-featured message broker and use it for
37478+
managing client subscriptions and broadcasting messages.
37479+
37480+
Check the message broker STOMP page (e.g.
37481+
http://www.rabbitmq.com/stomp.html[RabbitMQ],
37482+
http://activemq.apache.org/stomp.html[ActiveMQ]), install and run the broker with
37483+
STOMP support enabled. Then enable the STOMP broker relay in the Spring
37484+
configuration as an alternative to the simple broker.
37485+
37486+
Below is example configuration:
37487+
37488+
[source,java,indent=0]
37489+
[subs="verbatim,quotes"]
37490+
----
37491+
@Configuration
37492+
@EnableWebSocketMessageBroker
37493+
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
37494+
37495+
@Override
37496+
public void registerStompEndpoints(StompEndpointRegistry registry) {
37497+
registry.addEndpoint("/portfolio").withSockJS();
37498+
}
37499+
37500+
@Override
37501+
public void configureMessageBroker(MessageBrokerRegistry registry) {
37502+
registry.enableStompBrokerRelay("/topic/", "/queue/");
37503+
registry.setApplicationDestinationPrefixes("/app");
37504+
}
37505+
37506+
}
37507+
----
37508+
37509+
XML configuration equivalent:
37510+
37511+
[source,xml,indent=0]
37512+
[subs="verbatim,quotes,attributes"]
37513+
----
37514+
<beans xmlns="http://www.springframework.org/schema/beans"
37515+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
37516+
xmlns:websocket="http://www.springframework.org/schema/websocket"
37517+
xsi:schemaLocation="
37518+
http://www.springframework.org/schema/beans
37519+
http://www.springframework.org/schema/beans/spring-beans.xsd
37520+
http://www.springframework.org/schema/websocket
37521+
http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd">
37522+
37523+
<websocket:message-broker application-destination-prefix="/app">
37524+
<websocket:stomp-endpoint path="/portfolio" />
37525+
<websocket:sockjs/>
37526+
</websocket:stomp-endpoint>
37527+
<websocket:stomp-broker-relay prefix="/topic,/queue" />
37528+
</websocket:message-broker>
37529+
37530+
</beans>
37531+
----
37532+
37533+
The STOMP "broker relay" from the above configuration manages TCP connections
37534+
to the external broker, and forwards matching messages to it. Likewise any
37535+
messages received from the external broker are matched and routed to connected
37536+
clients.
37537+
37538+
In effect, messages are now broadcast through a full-featured, robust and
37539+
scalable message broker.
37540+
37541+
[[websocket-stomp-handle-user]]
37542+
===== Handling Messages to User Destinations
37543+
37544+
An application can also send messages targeting a specific user.
37545+
37546+
To do so a user must first be authenticated. Although the STOMP `CONNECT` frame
37547+
has authentication headers when used over WebSocket, it's simpler to use
37548+
the same HTTP-based authentication already used to secure the application.
37549+
37550+
An application can use Spring Security for example as usual protecting the
37551+
HTTP URLs of the STOMP WebSocket endpoint. The authenticanted user will then
37552+
be added as a header to all messages resulting on that STOMP/WebSocket session.
37553+
37554+
Spring supports STOMP destinations prefixed with "/user/". For example a client
37555+
can subscribe to "/user/position-updates". Such destinations are handled by
37556+
the `UserDestinationMessageHandler` and transformed into a destination unique
37557+
to the user's session, e.g. "/user/position-updates-123". This provides the
37558+
convenience of subscribing to a simple destination while also ensuring that it
37559+
doesn't collide with any other user that also subscribes to "/user/position-updates"
37560+
in order to receive position updates unique to them.
37561+
37562+
Similarly on the sending side, messages can be sent to destination
37563+
"/user/{username}/position-updates", which in turn will be translated into the
37564+
destination belonging to the specified user by name.
37565+
37566+
This allows any component within the application to send messages to a specific
37567+
user without necessarily knowing anything more than their name and a generic
37568+
destination.
3737337569

3737437570

3737537571
[[spring-integration]]

0 commit comments

Comments
 (0)