Skip to content

Commit

Permalink
Update @SendToUser and related code
Browse files Browse the repository at this point in the history
Issue: SPR-11506
  • Loading branch information
rstoyanchev committed May 1, 2014
1 parent 088b80f commit 9598a1e
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 53 deletions.
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 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.
Expand All @@ -26,33 +26,37 @@
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;

/**
* Annotation that can be used on methods processing an input message to indicate that the
* method's return value should be converted to a {@link Message} and sent to the
* specified destination with the prefix <code>"/user/{username}"</code> automatically
* prepended with the user information expected to be the input message header
* {@link SimpMessageHeaderAccessor#USER_HEADER}. Such user destinations may need to be
* further resolved to actual destinations.
* Annotation that indicates 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 {@code "/user/{username}"} where the user
* name is extracted from the headers of the input message being handled.
*
* @author Rossen Stoyanchev
* @since 4.0
* @see org.springframework.messaging.handler.annotation.SendTo
* @see org.springframework.messaging.simp.annotation.support.SendToMethodReturnValueHandler
* @see org.springframework.messaging.simp.user.UserDestinationMessageHandler
* @see org.springframework.messaging.simp.SimpMessageHeaderAccessor#getUser()
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SendToUser {

/**
* The destination for a message based on the return value of a method.
* One or more destinations to send a message to. If left unspecified, a
* default destination is selected based on the destination of the input
* message being handled.
* @see org.springframework.messaging.simp.annotation.support.SendToMethodReturnValueHandler
*/
String[] value() default {};

/**
* A flag indicating whether the message is to be sent to a particular user session.
*
* Whether messages should be sent to all sessions associated with the user
* or only to the session of the input message being handled.
*
* <p>By default this is set to {@code true} in which case messages are
* broadcast to all sessions.
*/
boolean singleSession() default false;

boolean broadcast() default true;

}
Expand Up @@ -16,9 +16,6 @@

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

import java.lang.annotation.Annotation;
import java.security.Principal;

import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.Message;
Expand All @@ -36,6 +33,9 @@
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.lang.annotation.Annotation;
import java.security.Principal;

/**
* A {@link HandlerMethodReturnValueHandler} for sending to destinations specified in a
* {@link SendTo} or {@link SendToUser} method-level annotations.
Expand Down Expand Up @@ -148,11 +148,11 @@ public void handleReturnValue(Object returnValue, MethodParameter returnType, Me
String user = getUserName(message, headers);
String[] destinations = getTargetDestinations(sendToUser, message, this.defaultUserDestinationPrefix);
for (String destination : destinations) {
if (sendToUser.singleSession()) {
this.messagingTemplate.convertAndSendToUser(userName, destination, returnValue, createHeaders(sessionId));
if (sendToUser.broadcast()) {
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue);
}
else }
this.messagingTemplate.convertAndSendToUser(userName, destination, returnValue);
else {
this.messagingTemplate.convertAndSendToUser(user, destination, returnValue, createHeaders(sessionId));
}
}
return;
Expand Down
Expand Up @@ -123,6 +123,7 @@ private DestinationInfo parseUserDestination(Message<?> message) {
SimpMessageType messageType = SimpMessageHeaderAccessor.getMessageType(headers);
String destination = SimpMessageHeaderAccessor.getDestination(headers);
Principal principal = SimpMessageHeaderAccessor.getUser(headers);
String sessionId = SimpMessageHeaderAccessor.getSessionId(headers);

String destinationWithoutPrefix;
String subscribeDestination;
Expand All @@ -137,7 +138,6 @@ private DestinationInfo parseUserDestination(Message<?> message) {
logger.error("Ignoring message, no principal info available");
return null;
}
String sessionId = SimpMessageHeaderAccessor.getSessionId(headers);
if (sessionId == null) {
logger.error("Ignoring message, no session id available");
return null;
Expand All @@ -158,12 +158,8 @@ else if (SimpMessageType.MESSAGE.equals(messageType)) {
subscribeDestination = this.destinationPrefix.substring(0, startIndex-1) + destinationWithoutPrefix;
user = destination.substring(startIndex, endIndex);
user = StringUtils.replace(user, "%2F", "/");
if (headers.getSessionId() == null){
sessionIds = this.userSessionRegistry.getSessionIds(user);
} else {
sessionIds = Collections.singleton(headers.getSessionId());
}

sessionIds = (sessionId != null ?
Collections.singleton(sessionId) : this.userSessionRegistry.getSessionIds(user));
}
else {
if (logger.isTraceEnabled()) {
Expand Down
Expand Up @@ -102,14 +102,14 @@ public void setup() throws Exception {

method = this.getClass().getDeclaredMethod("handleAndSendToUser");
this.sendToUserReturnType = new MethodParameter(method, -1);

method = this.getClass().getDeclaredMethod("handleAndSendToUserSingleSession");
this.sendToUserSingleSessionReturnType = new MethodParameter(method, -1);

method = this.getClass().getDeclaredMethod("handleAndSendToUserDefaultDestination");
this.sendToUserDefaultDestReturnType = new MethodParameter(method, -1);
method = this.getClass().getDeclaredMethod("handleAndSendToUserSingleSessionDefaultDestination");

method = this.getClass().getDeclaredMethod("handleAndSendToUserDefaultDestinationSingleSession");
this.sendToUserSingleSessionDefaultDestReturnType = new MethodParameter(method, -1);
}

Expand Down Expand Up @@ -231,16 +231,16 @@ public void sendToUser() throws Exception {
assertNull(headers.getSubscriptionId());
assertEquals("/user/" + user.getName() + "/dest2", headers.getDestination());
}

@Test
public void sendToUserSingleSession() throws Exception {

when(this.messageChannel.send(any(Message.class))).thenReturn(true);

String sessionId = "sess1";
TestUser user = new TestUser();
Message<?> inputMessage = createInputMessage(sessionId, "sub1", null, user);
this.handler.handleReturnValue(payloadContent, this.sendToUserSingleSessionReturnType, inputMessage);
Message<?> inputMessage = createInputMessage(sessionId, "sub1", null, null, user);
this.handler.handleReturnValue(PAYLOAD, this.sendToUserSingleSessionReturnType, inputMessage);

verify(this.messageChannel, times(2)).send(this.messageCaptor.capture());

Expand Down Expand Up @@ -296,16 +296,16 @@ public void sendToUserDefaultDestination() throws Exception {
assertNull(headers.getSubscriptionId());
assertEquals("/user/" + user.getName() + "/queue/dest", headers.getDestination());
}

@Test
public void sendToUserDefaultDestinationSingleSession() throws Exception {

when(this.messageChannel.send(any(Message.class))).thenReturn(true);

String sessionId = "sess1";
TestUser user = new TestUser();
Message<?> inputMessage = createInputMessage(sessionId, "sub1", "/dest", user);
this.handler.handleReturnValue(payloadContent, this.sendToUserSingleSessionDefaultDestReturnType, inputMessage);
Message<?> inputMessage = createInputMessage(sessionId, "sub1", "/app", "/dest", user);
this.handler.handleReturnValue(PAYLOAD, this.sendToUserSingleSessionDefaultDestReturnType, inputMessage);

verify(this.messageChannel, times(1)).send(this.messageCaptor.capture());

Expand All @@ -328,16 +328,8 @@ public void testHeadersToSendToUser() throws Exception {

handler.handleReturnValue(PAYLOAD, this.sendToUserDefaultDestReturnType, inputMessage);

ArgumentCaptor<MessageHeaders> captor = ArgumentCaptor.forClass(MessageHeaders.class);
verify(messagingTemplate).convertAndSendToUser(eq("joe"), eq("/queue/dest"), eq(PAYLOAD), captor.capture());

SimpMessageHeaderAccessor headerAccessor =
MessageHeaderAccessor.getAccessor(captor.getValue(), SimpMessageHeaderAccessor.class);

assertNotNull(headerAccessor);
assertTrue(headerAccessor.isMutable());
assertEquals("sess1", headerAccessor.getSessionId());
assertNull("Subscription id should not be copied", headerAccessor.getSubscriptionId());
verify(messagingTemplate).convertAndSendToUser(eq("joe"), eq("/queue/dest"), eq(PAYLOAD));
verifyNoMoreInteractions(messagingTemplate);
}


Expand Down Expand Up @@ -376,38 +368,45 @@ public String getDestinationUserName() {
}
}

@SuppressWarnings("unused")
public String handleNoAnnotations() {
return PAYLOAD;
}

@SuppressWarnings("unused")
@SendTo
public String handleAndSendToDefaultDestination() {
return PAYLOAD;
}

@SuppressWarnings("unused")
@SendTo({"/dest1", "/dest2"})
public String handleAndSendTo() {
return PAYLOAD;
}

@SuppressWarnings("unused")
@SendToUser
public String handleAndSendToUserDefaultDestination() {
return PAYLOAD;
}

@SendToUser(singleSession=true)
public String handleAndSendToUserSingleSessionDefaultDestination() {
return payloadContent;

@SuppressWarnings("unused")
@SendToUser(broadcast=false)
public String handleAndSendToUserDefaultDestinationSingleSession() {
return PAYLOAD;
}

@SuppressWarnings("unused")
@SendToUser({"/dest1", "/dest2"})
public String handleAndSendToUser() {
return PAYLOAD;
}

@SendToUser(value={"/dest1", "/dest2"}, singleSession=true)

@SuppressWarnings("unused")
@SendToUser(value={"/dest1", "/dest2"}, broadcast=false)
public String handleAndSendToUserSingleSession() {
return payloadContent;
return PAYLOAD;
}

}

0 comments on commit 9598a1e

Please sign in to comment.