@@ -1296,18 +1296,53 @@ See <<websocket-stomp-handle-send>>.
1296
1296
[[websocket-stomp-subscribe-mapping]]
1297
1297
==== `@SubscribeMapping`
1298
1298
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>>:
1307
1323
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.
1311
1346
1312
1347
1313
1348
[[websocket-stomp-exception-handler]]
@@ -1968,9 +2003,8 @@ Note that this incurs a small performance overhead, so enable it only if require
1968
2003
1969
2004
1970
2005
1971
-
1972
2006
[[websocket-stomp-appplication-context-events]]
1973
- === Events and Interception
2007
+ === Events
1974
2008
1975
2009
Several `ApplicationContext` events (listed below) are published and can be
1976
2010
received by implementing Spring's `ApplicationListener` interface.
@@ -2010,10 +2044,15 @@ will typically notice the broker is not responding within 10 seconds. Clients ne
2010
2044
implement their own reconnect logic.
2011
2045
====
2012
2046
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:
2017
2056
2018
2057
[source,java,indent=0]
2019
2058
[subs="verbatim,quotes"]
@@ -2035,7 +2074,7 @@ to access information about the message.
2035
2074
[source,java,indent=0]
2036
2075
[subs="verbatim,quotes"]
2037
2076
----
2038
- public class MyChannelInterceptor extends ChannelInterceptorAdapter {
2077
+ public class MyChannelInterceptor implements ChannelInterceptor {
2039
2078
2040
2079
@Override
2041
2080
public Message<?> preSend(Message<?> message, MessageChannel channel) {
@@ -2047,6 +2086,12 @@ to access information about the message.
2047
2086
}
2048
2087
----
2049
2088
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
+
2050
2095
Note that just like with the `SesionDisconnectEvent` above, a DISCONNECT message
2051
2096
may have been sent from the client, or it may also be automatically generated when
2052
2097
the WebSocket session is closed. In some cases an interceptor may intercept this
0 commit comments