Consolidate SendTo vs SendToUser detection

SendTo and SendToUser are treated as mutually exclusive. The addition of
type-level support in 4.3. RC1 however did not consider the possibility
to mix and match of the two betwee type and method level.

This commit consolidates the detection of SendTo and SendToUser
annotations and considers them all together.

Issue: SPR-14238
This commit is contained in:
Rossen Stoyanchev 2016-05-03 12:30:46 -04:00
parent 05b29a4a17
commit 46e41a9d94
2 changed files with 102 additions and 25 deletions

View File

@ -151,9 +151,10 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
MessageHeaders headers = message.getHeaders();
String sessionId = SimpMessageHeaderAccessor.getSessionId(headers);
PlaceholderResolver varResolver = initVarResolver(headers);
SendToUser sendToUser = getSendToUser(returnType);
Object annotation = findAnnotation(returnType);
if (sendToUser != null) {
if (annotation != null && annotation instanceof SendToUser) {
SendToUser sendToUser = (SendToUser) annotation;
boolean broadcast = sendToUser.broadcast();
String user = getUserName(message, headers);
if (user == null) {
@ -177,7 +178,7 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
}
}
else {
SendTo sendTo = getSendTo(returnType);
SendTo sendTo = (SendTo) annotation;
String[] destinations = getTargetDestinations(sendTo, message, this.defaultDestinationPrefix);
for (String destination : destinations) {
destination = this.placeholderHelper.replacePlaceholders(destination, varResolver);
@ -186,6 +187,35 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH
}
}
private Object findAnnotation(MethodParameter returnType) {
Annotation[] annot = new Annotation[4];
annot[0] = AnnotatedElementUtils.findMergedAnnotation(returnType.getMethod(), SendToUser.class);
annot[1] = AnnotatedElementUtils.findMergedAnnotation(returnType.getMethod(), SendTo.class);
annot[2] = AnnotatedElementUtils.findMergedAnnotation(returnType.getDeclaringClass(), SendToUser.class);
annot[3] = AnnotatedElementUtils.findMergedAnnotation(returnType.getDeclaringClass(), SendTo.class);
if (annot[0] != null && !ObjectUtils.isEmpty(((SendToUser) annot[0]).value())) {
return annot[0];
}
if (annot[1] != null && !ObjectUtils.isEmpty(((SendTo) annot[1]).value())) {
return annot[1];
}
if (annot[2] != null && !ObjectUtils.isEmpty(((SendToUser) annot[2]).value())) {
return annot[2];
}
if (annot[3] != null && !ObjectUtils.isEmpty(((SendTo) annot[3]).value())) {
return annot[3];
}
for (int i=0; i < 4; i++) {
if (annot[i] != null) {
return annot[i];
}
}
return null;
}
private SendToUser getSendToUser(MethodParameter returnType) {
SendToUser annot = AnnotatedElementUtils.findMergedAnnotation(returnType.getMethod(), SendToUser.class);
if (annot != null && !ObjectUtils.isEmpty(annot.value())) {

View File

@ -287,6 +287,39 @@ public class SendToMethodReturnValueHandlerTests {
assertResponse(this.userDefaultOverrideAnnotation, sessionId, 1, "/user/sess1/dest4");
}
@Test // SPR-14238
public void sendToUserWithSendToDefaultOverride() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
Class<?> clazz = SendToUserWithSendToOverrideTestBean.class;
Method method = clazz.getDeclaredMethod("handleAndSendToDefaultDestination");
MethodParameter parameter = new SynthesizingMethodParameter(method, -1);
String sessionId = "sess1";
Message<?> inputMessage = createMessage(sessionId, "sub1", null, null, null);
this.handler.handleReturnValue(PAYLOAD, parameter, inputMessage);
verify(this.messageChannel, times(1)).send(this.messageCaptor.capture());
assertResponse(parameter, sessionId, 0, "/user/sess1/dest-default");
}
@Test // SPR-14238
public void sendToUserWithSendToOverride() throws Exception {
given(this.messageChannel.send(any(Message.class))).willReturn(true);
Class<?> clazz = SendToUserWithSendToOverrideTestBean.class;
Method method = clazz.getDeclaredMethod("handleAndSendToOverride");
MethodParameter parameter = new SynthesizingMethodParameter(method, -1);
String sessionId = "sess1";
Message<?> inputMessage = createMessage(sessionId, "sub1", null, null, null);
this.handler.handleReturnValue(PAYLOAD, parameter, inputMessage);
verify(this.messageChannel, times(2)).send(this.messageCaptor.capture());
assertResponse(parameter, sessionId, 0, "/dest3");
assertResponse(parameter, sessionId, 1, "/dest4");
}
private void assertResponse(MethodParameter methodParameter, String sessionId,
int index, String destination) {
@ -552,7 +585,7 @@ public class SendToMethodReturnValueHandlerTests {
@SendTo
@Retention(RetentionPolicy.RUNTIME)
public @interface MySendTo {
@interface MySendTo {
@AliasFor(annotation = SendTo.class, attribute = "value")
String[] dest();
@ -560,7 +593,7 @@ public class SendToMethodReturnValueHandlerTests {
@SendToUser
@Retention(RetentionPolicy.RUNTIME)
public @interface MySendToUser {
@interface MySendToUser {
@AliasFor(annotation = SendToUser.class, attribute = "destinations")
String[] dest();
@ -568,47 +601,47 @@ public class SendToMethodReturnValueHandlerTests {
@SuppressWarnings("unused")
public String handleNoAnnotations() {
String handleNoAnnotations() {
return PAYLOAD;
}
@SendTo @SuppressWarnings("unused")
public String handleAndSendToDefaultDestination() {
String handleAndSendToDefaultDestination() {
return PAYLOAD;
}
@SendTo({"/dest1", "/dest2"}) @SuppressWarnings("unused")
public String handleAndSendTo() {
String handleAndSendTo() {
return PAYLOAD;
}
@SendTo("/topic/chat.message.filtered.{roomName}") @SuppressWarnings("unused")
public String handleAndSendToWithPlaceholders() {
String handleAndSendToWithPlaceholders() {
return PAYLOAD;
}
@SendToUser @SuppressWarnings("unused")
public String handleAndSendToUserDefaultDestination() {
String handleAndSendToUserDefaultDestination() {
return PAYLOAD;
}
@SendToUser(broadcast = false) @SuppressWarnings("unused")
public String handleAndSendToUserDefaultDestinationSingleSession() {
String handleAndSendToUserDefaultDestinationSingleSession() {
return PAYLOAD;
}
@SendToUser({"/dest1", "/dest2"}) @SuppressWarnings("unused")
public String handleAndSendToUser() {
String handleAndSendToUser() {
return PAYLOAD;
}
@SendToUser(destinations = { "/dest1", "/dest2" }, broadcast = false) @SuppressWarnings("unused")
public String handleAndSendToUserSingleSession() {
String handleAndSendToUserSingleSession() {
return PAYLOAD;
}
@JsonView(MyJacksonView1.class) @SuppressWarnings("unused")
public JacksonViewBean handleAndSendToJsonView() {
JacksonViewBean handleAndSendToJsonView() {
JacksonViewBean payload = new JacksonViewBean();
payload.setWithView1("with");
payload.setWithView2("with");
@ -620,17 +653,17 @@ public class SendToMethodReturnValueHandlerTests {
@MySendTo(dest = "/dest-default") @SuppressWarnings("unused")
private static class SendToTestBean {
public String handleNoAnnotation() {
String handleNoAnnotation() {
return PAYLOAD;
}
@SendTo
public String handleAndSendToDefaultDestination() {
String handleAndSendToDefaultDestination() {
return PAYLOAD;
}
@MySendTo(dest = {"/dest3", "/dest4"})
public String handleAndSendToOverride() {
String handleAndSendToOverride() {
return PAYLOAD;
}
}
@ -638,17 +671,31 @@ public class SendToMethodReturnValueHandlerTests {
@MySendToUser(dest = "/dest-default") @SuppressWarnings("unused")
private static class SendToUserTestBean {
public String handleNoAnnotation() {
String handleNoAnnotation() {
return PAYLOAD;
}
@SendToUser
public String handleAndSendToDefaultDestination() {
String handleAndSendToDefaultDestination() {
return PAYLOAD;
}
@MySendToUser(dest = {"/dest3", "/dest4"})
public String handleAndSendToOverride() {
String handleAndSendToOverride() {
return PAYLOAD;
}
}
@MySendToUser(dest = "/dest-default") @SuppressWarnings("unused")
private static class SendToUserWithSendToOverrideTestBean {
@SendTo
String handleAndSendToDefaultDestination() {
return PAYLOAD;
}
@MySendTo(dest = {"/dest3", "/dest4"})
String handleAndSendToOverride() {
return PAYLOAD;
}
}
@ -672,23 +719,23 @@ public class SendToMethodReturnValueHandlerTests {
return withView1;
}
public void setWithView1(String withView1) {
void setWithView1(String withView1) {
this.withView1 = withView1;
}
public String getWithView2() {
String getWithView2() {
return withView2;
}
public void setWithView2(String withView2) {
void setWithView2(String withView2) {
this.withView2 = withView2;
}
public String getWithoutView() {
String getWithoutView() {
return withoutView;
}
public void setWithoutView(String withoutView) {
void setWithoutView(String withoutView) {
this.withoutView = withoutView;
}
}