@@ -37058,7 +37058,7 @@ and server both need to understand how to interpret messages.
37058
37058
37059
37059
37060
37060
[[websocket-stomp-overview]]
37061
- ==== Overview of the STOMP Protocol
37061
+ ==== Overview of STOMP
37062
37062
http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple
37063
37063
messaging protocol originally created to connect to enterprise message brokers from
37064
37064
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":
37345
37345
The method accepts a String extracted from the payload of the message,
37346
37346
possibly converted based on its content type. The method can also return a
37347
37347
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):
37350
37352
37351
37353
[source,java,indent=0]
37352
37354
[subs="verbatim,quotes"]
@@ -37369,7 +37371,201 @@ which enriches the greeting with a timestamp and sends a new message to the
37369
37371
broker with destination "/topic/greetings". The broker then broadcasts the
37370
37372
message to all subscribed, connected clients.
37371
37373
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:
37372
37436
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.
37373
37569
37374
37570
37375
37571
[[spring-integration]]
0 commit comments