@SendToUser supported on the class level
Issue: SPR-12047
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
* Copyright 2002-2016 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.
|
||||
@@ -30,6 +30,9 @@ import org.springframework.core.annotation.AliasFor;
|
||||
* destination(s) prepended with <code>"/user/{username}"</code> where the user name
|
||||
* is extracted from the headers of the input message being handled.
|
||||
*
|
||||
* <p>The annotation may also be placed at class-level in which case all methods
|
||||
* in the class where the annotation applies will inherit it.
|
||||
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sam Brannen
|
||||
* @since 4.0
|
||||
@@ -37,7 +40,7 @@ import org.springframework.core.annotation.AliasFor;
|
||||
* @see org.springframework.messaging.simp.user.UserDestinationMessageHandler
|
||||
* @see org.springframework.messaging.simp.SimpMessageHeaderAccessor#getUser()
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface SendToUser {
|
||||
|
||||
@@ -134,7 +134,8 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
|
||||
public boolean supportsReturnType(MethodParameter returnType) {
|
||||
if (returnType.getMethodAnnotation(SendTo.class) != null ||
|
||||
AnnotationUtils.getAnnotation(returnType.getDeclaringClass(), SendTo.class) != null ||
|
||||
returnType.getMethodAnnotation(SendToUser.class) != null) {
|
||||
returnType.getMethodAnnotation(SendToUser.class) != null ||
|
||||
AnnotationUtils.getAnnotation(returnType.getDeclaringClass(), SendToUser.class) != null) {
|
||||
return true;
|
||||
}
|
||||
return (!this.annotationRequired);
|
||||
@@ -149,7 +150,7 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
String sessionId = SimpMessageHeaderAccessor.getSessionId(headers);
|
||||
PlaceholderResolver varResolver = initVarResolver(headers);
|
||||
SendToUser sendToUser = returnType.getMethodAnnotation(SendToUser.class);
|
||||
SendToUser sendToUser = getSendToUser(returnType);
|
||||
|
||||
if (sendToUser != null) {
|
||||
boolean broadcast = sendToUser.broadcast();
|
||||
@@ -184,6 +185,18 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
|
||||
}
|
||||
}
|
||||
|
||||
private SendToUser getSendToUser(MethodParameter returnType) {
|
||||
SendToUser annot = returnType.getMethodAnnotation(SendToUser.class);
|
||||
if (annot != null && !ObjectUtils.isEmpty((annot.value()))) {
|
||||
return annot;
|
||||
}
|
||||
SendToUser typeAnnot = AnnotationUtils.getAnnotation(returnType.getDeclaringClass(), SendToUser.class);
|
||||
if (typeAnnot != null && !ObjectUtils.isEmpty((typeAnnot.value()))) {
|
||||
return typeAnnot;
|
||||
}
|
||||
return (annot != null ? annot : typeAnnot);
|
||||
}
|
||||
|
||||
private SendTo getSendTo(MethodParameter returnType) {
|
||||
SendTo sendTo = returnType.getMethodAnnotation(SendTo.class);
|
||||
if (sendTo != null && !ObjectUtils.isEmpty((sendTo.value()))) {
|
||||
|
||||
@@ -91,6 +91,9 @@ public class SendToMethodReturnValueHandlerTests {
|
||||
private MethodParameter defaultNoAnnotation;
|
||||
private MethodParameter defaultEmptyAnnotation;
|
||||
private MethodParameter defaultOverrideAnnotation;
|
||||
private MethodParameter userDefaultNoAnnotation;
|
||||
private MethodParameter userDefaultEmptyAnnotation;
|
||||
private MethodParameter userDefaultOverrideAnnotation;
|
||||
|
||||
|
||||
@Before
|
||||
@@ -133,14 +136,23 @@ public class SendToMethodReturnValueHandlerTests {
|
||||
method = this.getClass().getDeclaredMethod("handleAndSendToJsonView");
|
||||
this.jsonViewReturnType = new SynthesizingMethodParameter(method, -1);
|
||||
|
||||
method = TestBean.class.getDeclaredMethod("handleNoAnnotation");
|
||||
method = SendToTestBean.class.getDeclaredMethod("handleNoAnnotation");
|
||||
this.defaultNoAnnotation = new SynthesizingMethodParameter(method, -1);
|
||||
|
||||
method = TestBean.class.getDeclaredMethod("handleAndSendToDefaultDestination");
|
||||
method = SendToTestBean.class.getDeclaredMethod("handleAndSendToDefaultDestination");
|
||||
this.defaultEmptyAnnotation = new SynthesizingMethodParameter(method, -1);
|
||||
|
||||
method = TestBean.class.getDeclaredMethod("handleAndSendToOverride");
|
||||
method = SendToTestBean.class.getDeclaredMethod("handleAndSendToOverride");
|
||||
this.defaultOverrideAnnotation = new SynthesizingMethodParameter(method, -1);
|
||||
|
||||
method = SendToUserTestBean.class.getDeclaredMethod("handleNoAnnotation");
|
||||
this.userDefaultNoAnnotation = new SynthesizingMethodParameter(method, -1);
|
||||
|
||||
method = SendToUserTestBean.class.getDeclaredMethod("handleAndSendToDefaultDestination");
|
||||
this.userDefaultEmptyAnnotation = new SynthesizingMethodParameter(method, -1);
|
||||
|
||||
method = SendToUserTestBean.class.getDeclaredMethod("handleAndSendToOverride");
|
||||
this.userDefaultOverrideAnnotation = new SynthesizingMethodParameter(method, -1);
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +166,10 @@ public class SendToMethodReturnValueHandlerTests {
|
||||
assertTrue(this.handler.supportsReturnType(this.defaultNoAnnotation));
|
||||
assertTrue(this.handler.supportsReturnType(this.defaultEmptyAnnotation));
|
||||
assertTrue(this.handler.supportsReturnType(this.defaultOverrideAnnotation));
|
||||
|
||||
assertTrue(this.handler.supportsReturnType(this.userDefaultNoAnnotation));
|
||||
assertTrue(this.handler.supportsReturnType(this.userDefaultEmptyAnnotation));
|
||||
assertTrue(this.handler.supportsReturnType(this.userDefaultOverrideAnnotation));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -230,6 +246,44 @@ public class SendToMethodReturnValueHandlerTests {
|
||||
assertResponse(this.defaultOverrideAnnotation, sessionId, 1, "/dest4");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendToUserClassDefaultNoAnnotation() throws Exception {
|
||||
given(this.messageChannel.send(any(Message.class))).willReturn(true);
|
||||
|
||||
String sessionId = "sess1";
|
||||
Message<?> inputMessage = createInputMessage(sessionId, "sub1", null, null, null);
|
||||
this.handler.handleReturnValue(PAYLOAD, this.userDefaultNoAnnotation, inputMessage);
|
||||
|
||||
verify(this.messageChannel, times(1)).send(this.messageCaptor.capture());
|
||||
assertResponse(this.userDefaultNoAnnotation, sessionId, 0, "/user/sess1/dest-default");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendToUserClassDefaultEmptyAnnotation() throws Exception {
|
||||
given(this.messageChannel.send(any(Message.class))).willReturn(true);
|
||||
|
||||
String sessionId = "sess1";
|
||||
Message<?> inputMessage = createInputMessage(sessionId, "sub1", null, null, null);
|
||||
this.handler.handleReturnValue(PAYLOAD, this.userDefaultEmptyAnnotation, inputMessage);
|
||||
|
||||
verify(this.messageChannel, times(1)).send(this.messageCaptor.capture());
|
||||
assertResponse(this.userDefaultEmptyAnnotation, sessionId, 0, "/user/sess1/dest-default");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendToUserClassDefaultOverride() throws Exception {
|
||||
given(this.messageChannel.send(any(Message.class))).willReturn(true);
|
||||
|
||||
String sessionId = "sess1";
|
||||
Message<?> inputMessage = createInputMessage(sessionId, "sub1", null, null, null);
|
||||
this.handler.handleReturnValue(PAYLOAD, this.userDefaultOverrideAnnotation, inputMessage);
|
||||
|
||||
verify(this.messageChannel, times(2)).send(this.messageCaptor.capture());
|
||||
assertResponse(this.userDefaultOverrideAnnotation, sessionId, 0, "/user/sess1/dest3");
|
||||
assertResponse(this.userDefaultOverrideAnnotation, sessionId, 1, "/user/sess1/dest4");
|
||||
}
|
||||
|
||||
|
||||
private void assertResponse(MethodParameter methodParameter, String sessionId,
|
||||
int index, String destination) {
|
||||
SimpMessageHeaderAccessor accessor = getCapturedAccessor(index);
|
||||
@@ -537,8 +591,8 @@ public class SendToMethodReturnValueHandlerTests {
|
||||
return payload;
|
||||
}
|
||||
|
||||
@SendTo("/dest-default")
|
||||
private static class TestBean {
|
||||
@SendTo("/dest-default") @SuppressWarnings("unused")
|
||||
private static class SendToTestBean {
|
||||
|
||||
public String handleNoAnnotation() {
|
||||
return PAYLOAD;
|
||||
@@ -556,6 +610,25 @@ public class SendToMethodReturnValueHandlerTests {
|
||||
|
||||
}
|
||||
|
||||
@SendToUser("/dest-default") @SuppressWarnings("unused")
|
||||
private static class SendToUserTestBean {
|
||||
|
||||
public String handleNoAnnotation() {
|
||||
return PAYLOAD;
|
||||
}
|
||||
|
||||
@SendToUser
|
||||
public String handleAndSendToDefaultDestination() {
|
||||
return PAYLOAD;
|
||||
}
|
||||
|
||||
@SendToUser({"/dest3", "/dest4"})
|
||||
public String handleAndSendToOverride() {
|
||||
return PAYLOAD;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private interface MyJacksonView1 {}
|
||||
private interface MyJacksonView2 {}
|
||||
|
||||
@@ -1700,7 +1700,8 @@ than their name and the generic destination. This is also supported through an
|
||||
annotation as well as a messaging template.
|
||||
|
||||
For example, a message-handling method can send messages to the user associated with
|
||||
the message being handled through the `@SendToUser` annotation:
|
||||
the message being handled through the `@SendToUser` annotation (also supported on
|
||||
the class-level to share a common destination):
|
||||
|
||||
[source,java,indent=0]
|
||||
[subs="verbatim,quotes"]
|
||||
|
||||
@@ -671,7 +671,7 @@ Spring 4.3 also improves the caching abstraction as follows:
|
||||
|
||||
=== WebSocket Messaging Improvements
|
||||
|
||||
* `@SendTo` can now be specified at class-level to share a common destination.
|
||||
* `@SendTo` and `@SendToUser` can now be specified at class-level to share a common destination.
|
||||
|
||||
=== Testing Improvements
|
||||
|
||||
|
||||
Reference in New Issue
Block a user