@@ -1296,18 +1296,53 @@ See <<websocket-stomp-handle-send>>.
12961296[[websocket-stomp-subscribe-mapping]]
12971297==== `@SubscribeMapping`
12981298
1299- `@SubscribeMapping` is similar to `@MessageMapping` but also narrows the mapping to
1300- subscription messages only. Methods with `@SubscribeMapping` support the same
1301- <<websocket-stomp-message-mapping,method arguments>> as `@MessageMapping` methods do.
1302- The main difference is that for the return value, in the absence of `@SendTo` and
1303- `@SendToUser`, a message is sent directly as a reply to the subscription, via the
1304- "clientOutboundChannel" channel. Effectively in this case the subscription is used as
1305- a one-time, request-reply message exchange with the subscription never stored.
1306- This is useful for loading data on startup and for initializing a front-end UI.
1299+ `@SubscribeMapping` is similar to `@MessageMapping` but narrows the mapping to
1300+ subscription messages only. It supports the same
1301+ <<websocket-stomp-message-mapping,method arguments>> as `@MessageMapping` does. However
1302+ for the return value, by default a message is sent directly to the client via
1303+ "clientOutboundChannel" in response to the subscription, and not to the broker via
1304+ "brokerChannel" as a broadcast to matching subscriptions. Adding `@SendTo` or
1305+ `@SendToUser` overrides this behavior and sends to the broker instead.
1306+
1307+ When is this useful? Let's assume the broker is mapped to "/topic" and "/queue" while
1308+ application controllers are mapped to "/app". In this setup, the broker *stores* all
1309+ subscriptions to "/topic" and "/queue" that are intended for *repeated* broadcasts, and
1310+ there is no need for the application to get involved. A client could also also subscribe to
1311+ some "/app" destination and a controller could return a value in response to that
1312+ subscription without involving the broker, effectively a *one-off*, *request-reply* exchange,
1313+ without storing or using the subscription again. One case for this is populating a UI
1314+ with initial data on startup.
1315+
1316+ When is this not useful? Do not try to map broker and controllers to the same destination
1317+ prefix unless you want both to process messages, including subscriptions, independently
1318+ for some reason. Inbound messages are handled in parallel. There are no guarantees whether
1319+ broker or controller will process a given message first. If the goal is to be notified
1320+ when a subscription is stored and ready for broadcasts, then a client should ask for a
1321+ receipt if the server supports it (simple broker does not). For example with the Java
1322+ <<websocket-stomp-client>>:
13071323
1308- If an `@SubscribeMapping` method is annotated with `@SendTo` and `@SendToUser` the return
1309- value is sent to the `"brokerChannel"` as usual, sending a message subscribers of the
1310- specified destination(s).
1324+ [source,java,indent=0]
1325+ [subs="verbatim,quotes"]
1326+ ----
1327+ @Autowired
1328+ private TaskScheduler messageBrokerTaskScheduler;
1329+
1330+ // During initialization..
1331+ stompClient.setTaskScheduler(this.messageBrokerTaskScheduler);
1332+
1333+ // When subscribing..
1334+ StompHeaders headers = new StompHeaders();
1335+ headers.setDestination("/topic/...");
1336+ headers.setReceipt("r1");
1337+ FrameHandler handler = ...;
1338+ stompSession.subscribe(headers, handler).addReceiptTask(() -> {
1339+ // Subscription ready...
1340+ });
1341+ ----
1342+
1343+ A server side option is <<websocket-stomp-interceptors,to register>> an
1344+ `ExecutorChannelInterceptor` on the `brokerChannel` and implement the `afterMessageHandled`
1345+ method that is invoked after messages, including subscriptions, have been handled.
13111346
13121347
13131348[[websocket-stomp-exception-handler]]
@@ -1968,9 +2003,8 @@ Note that this incurs a small performance overhead, so enable it only if require
19682003
19692004
19702005
1971-
19722006[[websocket-stomp-appplication-context-events]]
1973- === Events and Interception
2007+ === Events
19742008
19752009Several `ApplicationContext` events (listed below) are published and can be
19762010received by implementing Spring's `ApplicationListener` interface.
@@ -2010,10 +2044,15 @@ will typically notice the broker is not responding within 10 seconds. Clients ne
20102044implement their own reconnect logic.
20112045====
20122046
2013- The above events reflect points in the lifecycle of a STOMP connection. They're not meant
2014- to provide notification for every message sent from the client. Instead an application
2015- can register a `ChannelInterceptor` to intercept every incoming and outgoing STOMP message.
2016- For example to intercept inbound messages:
2047+
2048+
2049+ [[websocket-stomp-interceptors]]
2050+ === Interception
2051+
2052+ <<websocket-stomp-appplication-context-events>> provide notifications for the lifecycle
2053+ of a STOMP connection and not for every client message. Applications can also register a
2054+ `ChannelInterceptor` to intercept any message, and in any part of the processing chain.
2055+ For example to intercept inbound messages from clients:
20172056
20182057[source,java,indent=0]
20192058[subs="verbatim,quotes"]
@@ -2035,7 +2074,7 @@ to access information about the message.
20352074[source,java,indent=0]
20362075[subs="verbatim,quotes"]
20372076----
2038- public class MyChannelInterceptor extends ChannelInterceptorAdapter {
2077+ public class MyChannelInterceptor implements ChannelInterceptor {
20392078
20402079 @Override
20412080 public Message<?> preSend(Message<?> message, MessageChannel channel) {
@@ -2047,6 +2086,12 @@ to access information about the message.
20472086 }
20482087----
20492088
2089+ Applications may also implement `ExecutorChannelInterceptor` which is a sub-interface
2090+ of `ChannelInterceptor` with callbacks in the thread in which the messages are handled.
2091+ While a `ChannelInterceptor` is invoked once for per message sent to a channel, the
2092+ `ExecutorChannelInterceptor` provides hooks in the thread of each `MessageHandler`
2093+ subscribed to messages from the channel.
2094+
20502095Note that just like with the `SesionDisconnectEvent` above, a DISCONNECT message
20512096may have been sent from the client, or it may also be automatically generated when
20522097the WebSocket session is closed. In some cases an interceptor may intercept this
0 commit comments