@@ -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
3706237062http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple
3706337063messaging protocol originally created to connect to enterprise message brokers from
3706437064scripting 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":
3734537345The method accepts a String extracted from the payload of the message,
3734637346possibly converted based on its content type. The method can also return a
3734737347value, 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
3736937371broker with destination "/topic/greetings". The broker then broadcasts the
3737037372message 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