Skip to content

Commit 0ac6998

Browse files
committed
Refine destination semantics for msg-handling methods
After this change, annotated message handling methods configured to use a destination prefix (e.g. "/app") no longer have to include the prefix in their mapping. For example if a client sends a message to "/app/foo" the annotated methods should be mapped with @MessageMapping("/foo").
1 parent e1a46bb commit 0ac6998

20 files changed

+315
-116
lines changed

spring-messaging/src/main/java/org/springframework/messaging/MessageHeaders.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Collection;
2626
import java.util.Collections;
2727
import java.util.HashMap;
28+
import java.util.LinkedHashMap;
2829
import java.util.List;
2930
import java.util.Map;
3031
import java.util.Set;
@@ -149,7 +150,10 @@ public boolean equals(Object object) {
149150

150151
@Override
151152
public String toString() {
152-
return this.headers.toString();
153+
Map<String, Object> map = new LinkedHashMap<String, Object>(this.headers);
154+
map.put(ID, map.remove(ID)); // remove and add again at the end
155+
map.put(TIMESTAMP, map.remove(TIMESTAMP));
156+
return map.toString();
153157
}
154158

155159
/*

spring-messaging/src/main/java/org/springframework/messaging/handler/method/HandlerMethodReturnValueHandlerComposite.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.ArrayList;
2020
import java.util.List;
2121

22+
import org.apache.commons.logging.Log;
23+
import org.apache.commons.logging.LogFactory;
2224
import org.springframework.core.MethodParameter;
2325
import org.springframework.messaging.Message;
2426
import org.springframework.util.Assert;
@@ -30,6 +32,8 @@
3032
*/
3133
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
3234

35+
private static Log logger = LogFactory.getLog(HandlerMethodReturnValueHandlerComposite.class);
36+
3337
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<HandlerMethodReturnValueHandler>();
3438

3539

@@ -61,6 +65,9 @@ public boolean supportsReturnType(MethodParameter returnType) {
6165
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
6266
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
6367
if (handler.supportsReturnType(returnType)) {
68+
if (logger.isTraceEnabled()) {
69+
logger.trace("Processing return value with " + handler);
70+
}
6471
return handler;
6572
}
6673
}
@@ -72,7 +79,7 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType, Me
7279
throws Exception {
7380

7481
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
75-
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
82+
Assert.notNull(handler, "No handler for return value type [" + returnType.getParameterType().getName() + "]");
7683
handler.handleReturnValue(returnValue, returnType, message);
7784
}
7885

spring-messaging/src/main/java/org/springframework/messaging/handler/method/InvocableHandlerMethod.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,16 +104,17 @@ public final Object invoke(Message<?> message, Object... providedArgs) throws Ex
104104
Object[] args = getMethodArgumentValues(message, providedArgs);
105105

106106
if (logger.isTraceEnabled()) {
107-
StringBuilder builder = new StringBuilder("Invoking [");
108-
builder.append(this.getMethod().getName()).append("] method with arguments ");
109-
builder.append(Arrays.asList(args));
110-
logger.trace(builder.toString());
107+
StringBuilder sb = new StringBuilder("Invoking [");
108+
sb.append(this.getBeanType().getSimpleName()).append(".");
109+
sb.append(this.getMethod().getName()).append("] method with arguments ");
110+
sb.append(Arrays.asList(args));
111+
logger.trace(sb.toString());
111112
}
112113

113114
Object returnValue = invoke(args);
114115

115116
if (logger.isTraceEnabled()) {
116-
logger.trace("Method [" + this.getMethod().getName() + "] returned [" + returnValue + "]");
117+
logger.trace("Method returned [" + returnValue + "]");
117118
}
118119

119120
return returnValue;

spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/ReplyToMethodReturnValueHandler.java

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

1717
package org.springframework.messaging.simp.annotation.support;
1818

19+
import java.lang.annotation.Annotation;
20+
1921
import org.springframework.core.MethodParameter;
22+
import org.springframework.core.annotation.AnnotationUtils;
2023
import org.springframework.messaging.Message;
2124
import org.springframework.messaging.MessageChannel;
2225
import org.springframework.messaging.core.MessagePostProcessor;
@@ -27,6 +30,7 @@
2730
import org.springframework.messaging.simp.annotation.ReplyToUser;
2831
import org.springframework.messaging.support.MessageBuilder;
2932
import org.springframework.util.Assert;
33+
import org.springframework.util.ObjectUtils;
3034

3135

3236
/**
@@ -46,17 +50,23 @@ public class ReplyToMethodReturnValueHandler implements HandlerMethodReturnValue
4650

4751
private final SimpMessageSendingOperations messagingTemplate;
4852

53+
private final boolean annotationRequired;
54+
4955

50-
public ReplyToMethodReturnValueHandler(SimpMessageSendingOperations messagingTemplate) {
56+
public ReplyToMethodReturnValueHandler(SimpMessageSendingOperations messagingTemplate, boolean annotationRequired) {
5157
Assert.notNull(messagingTemplate, "messagingTemplate is required");
5258
this.messagingTemplate = messagingTemplate;
59+
this.annotationRequired = annotationRequired;
5360
}
5461

5562

5663
@Override
5764
public boolean supportsReturnType(MethodParameter returnType) {
58-
return ((returnType.getMethodAnnotation(ReplyTo.class) != null)
59-
|| (returnType.getMethodAnnotation(ReplyToUser.class) != null));
65+
if ((returnType.getMethodAnnotation(ReplyTo.class) != null) ||
66+
(returnType.getMethodAnnotation(ReplyToUser.class) != null)) {
67+
return true;
68+
}
69+
return (!this.annotationRequired);
6070
}
6171

6272
@Override
@@ -72,23 +82,32 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType, Me
7282
String sessionId = inputHeaders.getSessionId();
7383
MessagePostProcessor postProcessor = new SessionHeaderPostProcessor(sessionId);
7484

75-
ReplyTo replyTo = returnType.getMethodAnnotation(ReplyTo.class);
76-
if (replyTo != null) {
77-
for (String destination : replyTo.value()) {
78-
this.messagingTemplate.convertAndSend(destination, returnValue, postProcessor);
79-
}
80-
}
81-
8285
ReplyToUser replyToUser = returnType.getMethodAnnotation(ReplyToUser.class);
8386
if (replyToUser != null) {
8487
if (inputHeaders.getUser() == null) {
8588
throw new MissingSessionUserException(inputMessage);
8689
}
8790
String user = inputHeaders.getUser().getName();
88-
for (String destination : replyToUser.value()) {
91+
for (String destination : getDestinations(replyToUser, inputHeaders.getDestination())) {
8992
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue, postProcessor);
9093
}
94+
return;
95+
}
96+
97+
ReplyTo replyTo = returnType.getMethodAnnotation(ReplyTo.class);
98+
if (replyTo != null) {
99+
for (String destination : getDestinations(replyTo, inputHeaders.getDestination())) {
100+
this.messagingTemplate.convertAndSend(destination, returnValue, postProcessor);
101+
}
102+
return;
91103
}
104+
105+
this.messagingTemplate.convertAndSend(inputHeaders.getDestination(), returnValue, postProcessor);
106+
}
107+
108+
private String[] getDestinations(Annotation annot, String inputDestination) {
109+
String[] destinations = (String[]) AnnotationUtils.getValue(annot);
110+
return ObjectUtils.isEmpty(destinations) ? new String[] { inputDestination } : destinations;
92111
}
93112

94113

@@ -107,4 +126,10 @@ public Message<?> postProcessMessage(Message<?> message) {
107126
return MessageBuilder.withPayloadAndHeaders(message.getPayload(), headers).build();
108127
}
109128
}
129+
130+
@Override
131+
public String toString() {
132+
return "ReplyToMethodReturnValueHandler [annotationRequired=" + annotationRequired + "]";
133+
}
134+
110135
}

spring-messaging/src/main/java/org/springframework/messaging/simp/config/MessageBrokerConfigurer.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,37 @@ public MessageBrokerConfigurer(MessageChannel webSocketResponseChannel) {
4747
this.webSocketResponseChannel = webSocketResponseChannel;
4848
}
4949

50+
/**
51+
* Enable a simple message broker and configure one or more prefixes to filter
52+
* destinations targeting the broker (e.g. destinations prefixed with "/topic").
53+
*/
5054
public SimpleBrokerRegistration enableSimpleBroker(String... destinationPrefixes) {
5155
this.simpleBroker = new SimpleBrokerRegistration(this.webSocketResponseChannel, destinationPrefixes);
5256
return this.simpleBroker;
5357
}
5458

59+
/**
60+
* Enable a STOMP broker relay and configure the destination prefixes supported by the
61+
* message broker. Check the STOMP documentation of the message broker for supported
62+
* destinations.
63+
*/
5564
public StompBrokerRelayRegistration enableStompBrokerRelay(String... destinationPrefixes) {
5665
this.stompRelay = new StompBrokerRelayRegistration(this.webSocketResponseChannel, destinationPrefixes);
5766
return this.stompRelay;
5867
}
5968

69+
/**
70+
* Configure one or more prefixes to filter destinations targeting annotated
71+
* application methods. For example destinations prefixed with "/app" may be processed
72+
* by annotated application methods while other destinations may target the message
73+
* broker (e.g. "/topic", "/queue").
74+
* <p>
75+
* When messages are processed, the matching prefix is removed from the destination in
76+
* order to form the lookup path. This means annotations should not contain the
77+
* destination prefix.
78+
* <p>
79+
* Prefixes that do not have a trailing slash will have one automatically appended.
80+
*/
6081
public MessageBrokerConfigurer setAnnotationMethodDestinationPrefixes(String... destinationPrefixes) {
6182
this.annotationMethodDestinationPrefixes = destinationPrefixes;
6283
return this;

spring-messaging/src/main/java/org/springframework/messaging/simp/handler/AbstractBrokerMessageHandler.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public final void handleMessage(Message<?> message) {
141141
}
142142

143143
if (logger.isTraceEnabled()) {
144-
logger.trace("Processing message: " + message);
144+
logger.trace("Message " + message);
145145
}
146146

147147
handleMessageInternal(message);

spring-messaging/src/main/java/org/springframework/messaging/simp/handler/AbstractSubscriptionRegistry.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ public final void registerSubscription(Message<?> message) {
5656
return;
5757
}
5858
if (logger.isDebugEnabled()) {
59-
logger.debug("Subscribe request: " + message);
59+
logger.debug("Adding subscription id=" + headers.getSubscriptionId()
60+
+ ", destination=" + headers.getDestination());
6061
}
6162
addSubscriptionInternal(sessionId, subscriptionId, destination, message);
6263
}

0 commit comments

Comments
 (0)