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 d083baabf9..85d84f050f 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 @@ -48,4 +48,11 @@ public @interface SendToUser { */ String[] value() default {}; + /** + * A flag indicating whether the message is to be sent to a particular user session. + * + */ + boolean singleSession() default false; + + } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java index 962ee17a76..11c38daaf2 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandler.java @@ -148,7 +148,12 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH String user = getUserName(message, headers); String[] destinations = getTargetDestinations(sendToUser, message, this.defaultUserDestinationPrefix); for (String destination : destinations) { - this.messagingTemplate.convertAndSendToUser(user, destination, returnValue, createHeaders(sessionId)); + if (sendToUser.singleSession()) { + this.messagingTemplate.convertAndSendToUser(userName, destination, returnValue, createHeaders(sessionId)); + } + else } + this.messagingTemplate.convertAndSendToUser(userName, destination, returnValue); + } } return; } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/simp/user/DefaultUserDestinationResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/simp/user/DefaultUserDestinationResolver.java index 375b9309ba..55f010e4e7 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/simp/user/DefaultUserDestinationResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/simp/user/DefaultUserDestinationResolver.java @@ -158,7 +158,12 @@ public class DefaultUserDestinationResolver implements UserDestinationResolver { subscribeDestination = this.destinationPrefix.substring(0, startIndex-1) + destinationWithoutPrefix; user = destination.substring(startIndex, endIndex); user = StringUtils.replace(user, "%2F", "/"); - sessionIds = this.userSessionRegistry.getSessionIds(user); + if (headers.getSessionId() == null){ + sessionIds = this.userSessionRegistry.getSessionIds(user); + } else { + sessionIds = Collections.singleton(headers.getSessionId()); + } + } else { if (logger.isTraceEnabled()) { diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java index 0173bc1ee3..c187458e92 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/annotation/support/SendToMethodReturnValueHandlerTests.java @@ -74,7 +74,9 @@ public class SendToMethodReturnValueHandlerTests { private MethodParameter sendToReturnType; private MethodParameter sendToDefaultDestReturnType; private MethodParameter sendToUserReturnType; + private MethodParameter sendToUserSingleSessionReturnType; private MethodParameter sendToUserDefaultDestReturnType; + private MethodParameter sendToUserSingleSessionDefaultDestReturnType; @Before @@ -100,9 +102,15 @@ public class SendToMethodReturnValueHandlerTests { 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"); + this.sendToUserSingleSessionDefaultDestReturnType = new MethodParameter(method, -1); } @@ -211,6 +219,31 @@ public class SendToMethodReturnValueHandlerTests { verify(this.messageChannel, times(2)).send(this.messageCaptor.capture()); + Message message = this.messageCaptor.getAllValues().get(0); + SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message); + assertNull(headers.getSessionId()); + assertNull(headers.getSubscriptionId()); + assertEquals("/user/" + user.getName() + "/dest1", headers.getDestination()); + + message = this.messageCaptor.getAllValues().get(1); + headers = SimpMessageHeaderAccessor.wrap(message); + assertNull(headers.getSessionId()); + 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); + + verify(this.messageChannel, times(2)).send(this.messageCaptor.capture()); + Message message = this.messageCaptor.getAllValues().get(0); SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message); assertEquals(sessionId, headers.getSessionId()); @@ -257,6 +290,25 @@ public class SendToMethodReturnValueHandlerTests { verify(this.messageChannel, times(1)).send(this.messageCaptor.capture()); + Message message = this.messageCaptor.getAllValues().get(0); + SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message); + assertNull(headers.getSessionId()); + 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); + + verify(this.messageChannel, times(1)).send(this.messageCaptor.capture()); + Message message = this.messageCaptor.getAllValues().get(0); SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message); assertEquals(sessionId, headers.getSessionId()); @@ -342,10 +394,20 @@ public class SendToMethodReturnValueHandlerTests { public String handleAndSendToUserDefaultDestination() { return PAYLOAD; } + + @SendToUser(singleSession=true) + public String handleAndSendToUserSingleSessionDefaultDestination() { + return payloadContent; + } @SendToUser({"/dest1", "/dest2"}) public String handleAndSendToUser() { return PAYLOAD; } + + @SendToUser(value={"/dest1", "/dest2"}, singleSession=true) + public String handleAndSendToUserSingleSession() { + return payloadContent; + } } diff --git a/spring-messaging/src/test/java/org/springframework/messaging/simp/user/DefaultUserDestinationResolverTests.java b/spring-messaging/src/test/java/org/springframework/messaging/simp/user/DefaultUserDestinationResolverTests.java index 6a8496f554..34f768d405 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/simp/user/DefaultUserDestinationResolverTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/simp/user/DefaultUserDestinationResolverTests.java @@ -109,7 +109,7 @@ public class DefaultUserDestinationResolverTests { String userName = "http://joe.openid.example.org/"; this.registry.registerSessionId(userName, "openid123"); String destination = "/user/" + StringUtils.replace(userName, "/", "%2F") + "/queue/foo"; - Message message = createMessage(SimpMessageType.MESSAGE, this.user, SESSION_ID, destination); + Message message = createMessage(SimpMessageType.MESSAGE, this.user, null, destination); UserDestinationResult actual = this.resolver.resolveDestination(message); assertEquals(1, actual.getTargetDestinations().size());