From d196cdc5cde22308d60fb3eae1bc3e54ccf6bb0a Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Mon, 11 Jun 2018 12:39:47 -0400 Subject: [PATCH] Update docs on @SendTo and @SendToUser 1. Explain that both can be used on the same method 2. Better describe semantics for class vs method level 3. General improvements Issue: SPR-16336 --- .../messaging/handler/annotation/SendTo.java | 5 +- .../messaging/simp/annotation/SendToUser.java | 16 ++-- .../SimpAnnotationMethodMessageHandler.java | 10 +-- src/docs/asciidoc/web/websocket.adoc | 88 +++++++++---------- 4 files changed, 61 insertions(+), 58 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/SendTo.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/SendTo.java index 0a5a3f2753..83b3af200b 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/SendTo.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/SendTo.java @@ -32,9 +32,8 @@ import org.springframework.messaging.Message; * convey the destination to use for the reply. In that case, that destination * should take precedence. * - *

The annotation may also be placed at class-level if the provider supports - * it to indicate that all related methods should use this destination if none - * is specified otherwise. +*

This annotation may be placed class-level in which case it is inherited by + * methods of the class. * * @author Rossen Stoyanchev * @author Stephane Nicoll diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SendToUser.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SendToUser.java index 255e1238ea..33f5fac6d6 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SendToUser.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/SendToUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,13 +25,17 @@ import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; /** - * Annotation that indicates that the return value of a message-handling method - * should be sent as a {@link org.springframework.messaging.Message} to the specified - * destination(s) prepended with "/user/{username}" where the user name + * Indicates the return value of a message-handling method should be sent as a + * {@link org.springframework.messaging.Message} to the specified destination(s) + * further prepended with "/user/{username}" where the user name * is extracted from the headers of the input message being handled. * - *

The annotation may also be placed at class-level in which case all methods - * in the class where the annotation applies will inherit it. + *

Both {@code @SendTo} and {@code @SendToUser} may be used on the same method + * in which case a message is sent to the destinations of both annotations. + * + *

This annotation may be placed class-level in which case it is inherited + * by methods of the class. At the same time, method-level {@code @SendTo} or + * {@code @SendToUser} annotations override any such at the class level. * @author Rossen Stoyanchev * @author Sam Brannen diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java index 7ee2bdcf96..90b4a3d91e 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SimpAnnotationMethodMessageHandler.java @@ -330,15 +330,14 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan List handlers = new ArrayList<>(); // Single-purpose return value types + handlers.add(new ListenableFutureReturnValueHandler()); handlers.add(new CompletableFutureReturnValueHandler()); // Annotation-based return value types - SendToMethodReturnValueHandler sendToHandler = - new SendToMethodReturnValueHandler(this.brokerTemplate, true); - if (this.headerInitializer != null) { - sendToHandler.setHeaderInitializer(this.headerInitializer); - } + + SendToMethodReturnValueHandler sendToHandler = new SendToMethodReturnValueHandler(this.brokerTemplate, true); + sendToHandler.setHeaderInitializer(this.headerInitializer); handlers.add(sendToHandler); SubscriptionMethodReturnValueHandler subscriptionHandler = @@ -350,6 +349,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan handlers.addAll(getCustomReturnValueHandlers()); // catch-all + sendToHandler = new SendToMethodReturnValueHandler(this.brokerTemplate, false); sendToHandler.setHeaderInitializer(this.headerInitializer); handlers.add(sendToHandler); diff --git a/src/docs/asciidoc/web/websocket.adoc b/src/docs/asciidoc/web/websocket.adoc index b7627d82ba..3c36722719 100644 --- a/src/docs/asciidoc/web/websocket.adoc +++ b/src/docs/asciidoc/web/websocket.adoc @@ -1214,22 +1214,17 @@ methods as described next. [[websocket-stomp-message-mapping]] ==== `@MessageMapping` -The `@MessageMapping` annotation can be used on methods to route messages based on their +`@MessageMapping` can be used to annotate methods to route messages based on their destination. It is supported at the method level as well as at the type level. At type level `@MessageMapping` is used to express shared mappings across all methods in a controller. -By default destination mappings are expected to be Ant-style, path patterns, e.g. "/foo*", -"/foo/**". The patterns include support for template variables, e.g. "/foo/{id}", that can -be referenced with `@DestinationVariable` method arguments. +The mapping values are Ant-style path patterns by default, e.g. "/foo*", "/foo/**" +including support for template variables, e.g. "/foo/{id}", that can be referenced via +`@DestinationVariable` method arguments. Applications can also switch to a dot-separated +destination convention for mappings, as explained in <>. -[TIP] -==== -Applications can choose to switch to a dot-separated destination convention. -See <>. -==== - -`@MessageMapping` methods can have flexible signatures with the following arguments: +*Supported Method Arguments* [cols="1,2", options="header"] |=== @@ -1271,47 +1266,52 @@ Values will be converted to the declared method argument type as necessary. |=== -When an `@MessageMapping` method returns a value, by default the value is serialized to -a payload through a configured `MessageConverter`, and then sent as a `Message` to the -`"brokerChannel"` from where it is broadcast to subscribers. The destination of the -outbound message is the same as that of the inbound message but prefixed with `"/topic"`. +*Return Values* + +By default, the return value from an `@MessageMapping` method is serialized to a payload +through a matching `MessageConverter`, and sent as a `Message` to the `"brokerChannel"` +from where it is broadcast to subscribers. The destination of the outbound message is the +same as that of the inbound message but prefixed with `"/topic"`. + +The `@SendTo` and `@SendToUser` annotations can be used to customize the destination of +the output message. `@SendTo` is used to simply customize target destination, or to +specify multiple destinations. `@SendToUser` is used to direct the output message only +to the user associated with the input message, see <>. -You can use the `@SendTo` method annotation to customize the destination to send -the payload to. `@SendTo` can also be used at the class level to share a default target -destination to send messages to. `@SendToUser` is an variant for sending messages only to -the user associated with a message. See <> for details. +`@SendTo` and `@SendToUser` may both be used at the same time on the same method, and both +are supported at the class level in which case they act as a default for methods in the +class. However keep in mind that _any_ method-level `@SendTo` or `@SendToUser` annotations +override _any_ such annotations at the class level. -The return value from an `@MessageMapping` method may be wrapped with `ListenableFuture`, -`CompletableFuture`, or `CompletionStage` in order to produce the payload asynchronously. +Messages may be handled asynchronously and a `@MessageMapping` method may return +`ListenableFuture`, `CompletableFuture`, or `CompletionStage`. -As an alternative to returning a payload from an `@MessageMapping` method you can also -send messages using the `SimpMessagingTemplate`, which is also how return values are -handled under the covers. See <>. +Note that `@SendTo` and `@SendToUser` are merely a convenience that amounts to using the +`SimpMessagingTemplate` to send messages. If necessary, for more advanced scenarios, +`@MessageMapping` methods can fall back on using the `SimpMessagingTemplate` directly. +This can be done instead of, or possibly in addition to returning a value. +See <>. [[websocket-stomp-subscribe-mapping]] ==== `@SubscribeMapping` -The `@SubscribeMapping` annotation is used in combination with `@MessageMapping` in order -to narrow the mapping to subscription messages. In such scenarios, the `@MessageMapping` -annotation specifies the destination while `@SubscribeMapping` indicates interest in -subscription messages only. - -An `@SubscribeMapping` method is generally no different from any `@MessageMapping` -method with respect to mapping and input arguments. For example you can combine it with a -type-level `@MessageMapping` to express a shared destination prefix, and you can use the -same <> as any @MessageMapping` method. - -The key difference with `@SubscribeMapping` is that the return value of the method is -serialized as a payload and sent, not to the "brokerChannel" but to the -"clientOutboundChannel", effectively replying directly to the client rather than -broadcasting through the broker. This is useful for implementing one-off, request-reply -message exchanges, and never holding on to the subscription. A common scenario for this -pattern is application initialization when data must be loaded and presented. - -A `@SubscribeMapping` method can also be annotated with `@SendTo` in which case the -return value is sent to the `"brokerChannel"` with the explicitly specified target -destination. +`@SubscribeMapping` is used together with `@MessageMapping` to narrow the mapping to +subscription messages. In this scenario `@MessageMapping` expresses message destination +mappings for routing purposes, which can be done at the class or at the method level, +while `@SubscribeMapping` narrows the mapping to subscription messages only. + +Methods with `@MessageMapping` and `@SubscribeMapping` support the same +<> as methods annotated only with +`@MessageMapping` do. However for the return value, in the absence of `@SendTo` and +`@SendToUser`, a message is sent directly as a reply to the subscription, via the +"clientOutboundChannel" channel. Effectively the subscription is used as a one-time, +request-reply message exchange with the subscription never stored. This is useful for +loading data on startup and for initializing a front-end UI. + +If an `@SubscribeMapping` method is annotated with `@SendTo` and `@SendToUser` the return +value is sent to the `"brokerChannel"` as usual, sending a message subscribers of the +specified destination(s). [[websocket-stomp-exception-handler]]