@@ -1292,18 +1292,53 @@ handled under the covers. See <<websocket-stomp-handle-send>>.
1292
1292
[[websocket-stomp-subscribe-mapping]]
1293
1293
==== `@SubscribeMapping`
1294
1294
1295
- `@SubscribeMapping` is similar to `@MessageMapping` but also narrows the mapping to
1296
- subscription messages only. Methods with `@SubscribeMapping` support the same
1297
- <<websocket-stomp-message-mapping,method arguments>> as `@MessageMapping` methods do.
1298
- The main difference is that for the return value, in the absence of `@SendTo` and
1299
- `@SendToUser`, a message is sent directly as a reply to the subscription, via the
1300
- "clientOutboundChannel" channel. Effectively in this case the subscription is used as
1301
- a one-time, request-reply message exchange with the subscription never stored.
1302
- This is useful for loading data on startup and for initializing a front-end UI.
1295
+ `@SubscribeMapping` is similar to `@MessageMapping` but narrows the mapping to
1296
+ subscription messages only. It supports the same
1297
+ <<websocket-stomp-message-mapping,method arguments>> as `@MessageMapping` does. However
1298
+ for the return value, by default a message is sent directly to the client via
1299
+ "clientOutboundChannel" in response to the subscription, and not to the broker via
1300
+ "brokerChannel" as a broadcast to matching subscriptions. Adding `@SendTo` or
1301
+ `@SendToUser` overrides this behavior and sends to the broker instead.
1302
+
1303
+ When is this useful? Let's assume the broker is mapped to "/topic" and "/queue" while
1304
+ application controllers are mapped to "/app". In this setup, the broker *stores* all
1305
+ subscriptions to "/topic" and "/queue" that are intended for *repeated* broadcasts, and
1306
+ there is no need for the application to get involved. A client could also also subscribe to
1307
+ some "/app" destination and a controller could return a value in response to that
1308
+ subscription without involving the broker, effectively a *one-off*, *request-reply* exchange,
1309
+ without storing or using the subscription again. One case for this is populating a UI
1310
+ with initial data on startup.
1311
+
1312
+ When is this not useful? Do not try to map broker and controllers to the same destination
1313
+ prefix unless you want both to process messages, including subscriptions, independently
1314
+ for some reason. Inbound messages are handled in parallel. There are no guarantees whether
1315
+ broker or controller will process a given message first. If the goal is to be notified
1316
+ when a subscription is stored and ready for broadcasts, then a client should ask for a
1317
+ receipt if the server supports it (simple broker does not). For example with the Java
1318
+ <<websocket-stomp-client>>:
1303
1319
1304
- If an `@SubscribeMapping` method is annotated with `@SendTo` or `@SendToUser` the return
1305
- value is sent to the `"brokerChannel"` as usual, i.e. sending a message to subscribers
1306
- of the specified destination(s).
1320
+ [source,java,indent=0]
1321
+ [subs="verbatim,quotes"]
1322
+ ----
1323
+ @Autowired
1324
+ private TaskScheduler messageBrokerTaskScheduler;
1325
+
1326
+ // During initialization..
1327
+ stompClient.setTaskScheduler(this.messageBrokerTaskScheduler);
1328
+
1329
+ // When subscribing..
1330
+ StompHeaders headers = new StompHeaders();
1331
+ headers.setDestination("/topic/...");
1332
+ headers.setReceipt("r1");
1333
+ FrameHandler handler = ...;
1334
+ stompSession.subscribe(headers, handler).addReceiptTask(() -> {
1335
+ // Subscription ready...
1336
+ });
1337
+ ----
1338
+
1339
+ A server side option is <<websocket-stomp-interceptors,to register>> an
1340
+ `ExecutorChannelInterceptor` on the `brokerChannel` and implement the `afterMessageHandled`
1341
+ method that is invoked after messages, including subscriptions, have been handled.
1307
1342
1308
1343
1309
1344
[[websocket-stomp-exception-handler]]
@@ -1933,9 +1968,8 @@ Note that this incurs a small performance overhead, so enable it only if require
1933
1968
1934
1969
1935
1970
1936
-
1937
1971
[[websocket-stomp-appplication-context-events]]
1938
- === Events and Interception
1972
+ === Events
1939
1973
1940
1974
Several `ApplicationContext` events (listed below) are published and can be
1941
1975
received by implementing Spring's `ApplicationListener` interface.
@@ -1975,10 +2009,15 @@ will typically notice the broker is not responding within 10 seconds. Clients ne
1975
2009
implement their own reconnect logic.
1976
2010
====
1977
2011
1978
- The above events reflect points in the lifecycle of a STOMP connection. They're not meant
1979
- to provide notification for every message sent from the client. Instead an application
1980
- can register a `ChannelInterceptor` to intercept every incoming and outgoing STOMP message.
1981
- For example to intercept inbound messages:
2012
+
2013
+
2014
+ [[websocket-stomp-interceptors]]
2015
+ === Interception
2016
+
2017
+ <<websocket-stomp-appplication-context-events>> provide notifications for the lifecycle
2018
+ of a STOMP connection and not for every client message. Applications can also register a
2019
+ `ChannelInterceptor` to intercept any message, and in any part of the processing chain.
2020
+ For example to intercept inbound messages from clients:
1982
2021
1983
2022
[source,java,indent=0]
1984
2023
[subs="verbatim,quotes"]
@@ -2000,7 +2039,7 @@ to access information about the message.
2000
2039
[source,java,indent=0]
2001
2040
[subs="verbatim,quotes"]
2002
2041
----
2003
- public class MyChannelInterceptor extends ChannelInterceptorAdapter {
2042
+ public class MyChannelInterceptor implements ChannelInterceptor {
2004
2043
2005
2044
@Override
2006
2045
public Message<?> preSend(Message<?> message, MessageChannel channel) {
@@ -2012,6 +2051,12 @@ to access information about the message.
2012
2051
}
2013
2052
----
2014
2053
2054
+ Applications may also implement `ExecutorChannelInterceptor` which is a sub-interface
2055
+ of `ChannelInterceptor` with callbacks in the thread in which the messages are handled.
2056
+ While a `ChannelInterceptor` is invoked once for per message sent to a channel, the
2057
+ `ExecutorChannelInterceptor` provides hooks in the thread of each `MessageHandler`
2058
+ subscribed to messages from the channel.
2059
+
2015
2060
Note that just like with the `SesionDisconnectEvent` above, a DISCONNECT message
2016
2061
may have been sent from the client, or it may also be automatically generated when
2017
2062
the WebSocket session is closed. In some cases an interceptor may intercept this
0 commit comments