From 9598a1e2ef3467187eec928248df536ed8410c2a Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 1 May 2014 10:31:42 -0400 Subject: [PATCH] Update @SendToUser and related code Issue: SPR-11506 --- .../messaging/simp/annotation/SendToUser.java | 30 ++++++----- .../SendToMethodReturnValueHandler.java | 14 ++--- .../user/DefaultUserDestinationResolver.java | 10 ++-- .../SendToMethodReturnValueHandlerTests.java | 51 +++++++++---------- 4 files changed, 52 insertions(+), 53 deletions(-) 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 85d84f050fc..278f87f48d8 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 @@ -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. @@ -26,17 +26,16 @@ import org.springframework.messaging.Message; 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 "/user/{username}" 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) @@ -44,15 +43,20 @@ import org.springframework.messaging.simp.SimpMessageHeaderAccessor; 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. + * + *

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; } 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 11c38daaf2b..0aeedf1172d 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 @@ -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; @@ -36,6 +33,9 @@ import org.springframework.messaging.support.MessageHeaderInitializer; 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. @@ -148,11 +148,11 @@ public class SendToMethodReturnValueHandler implements HandlerMethodReturnValueH 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; 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 55f010e4e7f..a4bbce711d1 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 @@ -123,6 +123,7 @@ public class DefaultUserDestinationResolver implements UserDestinationResolver { SimpMessageType messageType = SimpMessageHeaderAccessor.getMessageType(headers); String destination = SimpMessageHeaderAccessor.getDestination(headers); Principal principal = SimpMessageHeaderAccessor.getUser(headers); + String sessionId = SimpMessageHeaderAccessor.getSessionId(headers); String destinationWithoutPrefix; String subscribeDestination; @@ -137,7 +138,6 @@ public class DefaultUserDestinationResolver implements UserDestinationResolver { 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; @@ -158,12 +158,8 @@ public class DefaultUserDestinationResolver implements UserDestinationResolver { 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()) { 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 c187458e922..fcfcbdd2a92 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 @@ -102,14 +102,14 @@ 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"); + + method = this.getClass().getDeclaredMethod("handleAndSendToUserDefaultDestinationSingleSession"); this.sendToUserSingleSessionDefaultDestReturnType = new MethodParameter(method, -1); } @@ -231,7 +231,7 @@ public class SendToMethodReturnValueHandlerTests { assertNull(headers.getSubscriptionId()); assertEquals("/user/" + user.getName() + "/dest2", headers.getDestination()); } - + @Test public void sendToUserSingleSession() throws Exception { @@ -239,8 +239,8 @@ public class SendToMethodReturnValueHandlerTests { 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()); @@ -296,7 +296,7 @@ public class SendToMethodReturnValueHandlerTests { assertNull(headers.getSubscriptionId()); assertEquals("/user/" + user.getName() + "/queue/dest", headers.getDestination()); } - + @Test public void sendToUserDefaultDestinationSingleSession() throws Exception { @@ -304,8 +304,8 @@ public class SendToMethodReturnValueHandlerTests { 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()); @@ -328,16 +328,8 @@ public class SendToMethodReturnValueHandlerTests { handler.handleReturnValue(PAYLOAD, this.sendToUserDefaultDestReturnType, inputMessage); - ArgumentCaptor 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); } @@ -376,38 +368,45 @@ public class SendToMethodReturnValueHandlerTests { } } + @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; } }