@@ -1292,18 +1292,53 @@ handled under the covers. See <<websocket-stomp-handle-send>>.
12921292[[websocket-stomp-subscribe-mapping]]
12931293==== `@SubscribeMapping`
12941294
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>>:
13031319
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.
13071342
13081343
13091344[[websocket-stomp-exception-handler]]
@@ -1933,9 +1968,8 @@ Note that this incurs a small performance overhead, so enable it only if require
19331968
19341969
19351970
1936-
19371971[[websocket-stomp-appplication-context-events]]
1938- === Events and Interception
1972+ === Events
19391973
19401974Several `ApplicationContext` events (listed below) are published and can be
19411975received by implementing Spring's `ApplicationListener` interface.
@@ -1975,10 +2009,15 @@ will typically notice the broker is not responding within 10 seconds. Clients ne
19752009implement their own reconnect logic.
19762010====
19772011
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:
19822021
19832022[source,java,indent=0]
19842023[subs="verbatim,quotes"]
@@ -2000,7 +2039,7 @@ to access information about the message.
20002039[source,java,indent=0]
20012040[subs="verbatim,quotes"]
20022041----
2003- public class MyChannelInterceptor extends ChannelInterceptorAdapter {
2042+ public class MyChannelInterceptor implements ChannelInterceptor {
20042043
20052044 @Override
20062045 public Message<?> preSend(Message<?> message, MessageChannel channel) {
@@ -2012,6 +2051,12 @@ to access information about the message.
20122051 }
20132052----
20142053
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+
20152060Note that just like with the `SesionDisconnectEvent` above, a DISCONNECT message
20162061may have been sent from the client, or it may also be automatically generated when
20172062the WebSocket session is closed. In some cases an interceptor may intercept this
0 commit comments