Rename @SubscribeEvent to @SubscribeMapping

Also @SubscribeMapping is now a method-level annotation only that can
be used in combination with a type-level @MessageMapping.

This method also documents supported method arguments and return value
types on @Subscribe- and @MessageMapping methods.
This commit is contained in:
Rossen Stoyanchev
2013-11-15 16:32:52 +00:00
parent 2597cd2339
commit 35b9b8400b
9 changed files with 110 additions and 72 deletions

View File

@@ -26,8 +26,54 @@ import org.springframework.messaging.Message;
/**
* Annotation for mapping a {@link Message} onto message handling methods by matching to
* the message destination.
* Annotation for mapping a {@link Message} onto message-handling methods by matching
* to the message destination. This annotation can also be used on the type-level in
* which case it defines a common destination prefix or pattern for all method-level
* annotations including method-level
* {@link org.springframework.messaging.simp.annotation.SubscribeMapping @SubscribeMapping}
* annotations.
* <p>
* Handler methods which are annotated with this annotation are allowed to have
* flexible signatures. They may have arguments of the following types, in arbitrary
* order:
* <ul>
* <li>{@link Message} to get access to the complete message being processed.</li>
* <li>{@link Payload}-annotated method arguments to extract the payload of
* a message and optionally convert it using a
* {@link org.springframework.messaging.support.converter.MessageConverter}.
* The presence of the annotation is not required since it is assumed by default
* for method arguments that are not annotated.</li>
* <li>{@link Header}-annotated method arguments to extract a specific
* header value along with type conversion with a
* {@link org.springframework.core.convert.converter.Converter} if necessary.</li>
* <li>{@link Headers}-annotated argument that must also be assignable to
* {@link java.util.Map} for getting access to all headers.</li>
* <li>{@link org.springframework.messaging.MessageHeaders} arguments for
* getting access to all headers.</li>
* <li>{@link org.springframework.messaging.support.MessageHeaderAccessor} or
* with STOMP over WebSocket support also sub-classes such as
* {@link org.springframework.messaging.simp.SimpMessageHeaderAccessor}
* for convenient access to all method arguments.</li>
* <li>{@link PathVariable}-annotated arguments for access to URI variable
* values extracted from the message destination (i.e. /hotels/{hotel}).
* Variable values will be converted to the declared method argument type.</li>
* <li>{@link java.security.Principal} method arguments are supported with
* STOMP over WebSocket messages. It reflects the user logged in to the
* WebSocket session on which the message was received. Regular HTTP-based
* authentication (e.g. Spring Security based) can be used to secure the
* HTTP handshake that initiates WebSocket sessions.</li>
* </ul>
* <p>
* By default the return value is wrapped as a message and sent to the destination
* specified with an {@link SendTo} method-level annotation.
* <p>
* STOMP over WebSocket: an {@link SendTo} annotation is not strictly required --
* by default the message will be sent to the same destination as the incoming
* message but with an additional prefix ("/topic" by default). It is also possible
* to use {@link org.springframework.messaging.simp.annotation.SendToUser} to
* have the message directed to a specific user only if connected.
* Also the return value is converted with a
* {@link org.springframework.messaging.support.converter.MessageConverter}.
*
* @author Rossen Stoyanchev
* @since 4.0

View File

@@ -24,16 +24,31 @@ import java.lang.annotation.Target;
/**
* Annotation for mapping subscription events onto specific handler methods based
* on the destination for the message (e.g. STOMP SUBSCRIBE message).
* Annotation for mapping subscription messages onto specific handler methods based
* on the destination of a subscription. Supported with STOMP over WebSocket only
* (e.g. STOMP SUBSCRIBE frame).
* <p>
* This is a method-level annotations that can be combined with a type-level
* {@link org.springframework.messaging.handler.annotation.MessageMapping @MessageMapping}
* <p>
* Supports the same method arguments as
* {@link org.springframework.messaging.handler.annotation.MessageMapping}, however
* subscription messages typically do not have a body.
* <p>
* The return value also follows the same rules as for
* {@link org.springframework.messaging.handler.annotation.MessageMapping} except if
* the method is not annotated with
* {@link org.springframework.messaging.handler.annotation.SendTo} or {@link SendToUser},
* the message is sent directly back to the connected user and does not pass through
* the message broker. This is useful for implementing a request-reply pattern.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SubscribeEvent {
public @interface SubscribeMapping {
/**
* Destination-based mapping expressed by this annotation.

View File

@@ -25,14 +25,14 @@ import org.springframework.messaging.handler.method.HandlerMethodReturnValueHand
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.annotation.SubscribeEvent;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.Assert;
/**
* A {@link HandlerMethodReturnValueHandler} for replying directly to a subscription. It
* supports methods annotated with {@link SubscribeEvent} unless they're also annotated
* supports methods annotated with {@link org.springframework.messaging.simp.annotation.SubscribeMapping} unless they're also annotated
* with {@link SendTo} or {@link SendToUser}.
* <p>
* The value returned from the method is converted, and turned to a {@link Message} and
@@ -55,7 +55,7 @@ public class SubscriptionMethodReturnValueHandler implements HandlerMethodReturn
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ((returnType.getMethodAnnotation(SubscribeEvent.class) != null)
return ((returnType.getMethodAnnotation(SubscribeMapping.class) != null)
&& (returnType.getMethodAnnotation(SendTo.class) == null)
&& (returnType.getMethodAnnotation(SendToUser.class) == null));
}

View File

@@ -49,7 +49,7 @@ import org.springframework.messaging.handler.method.HandlerMethodReturnValueHand
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SubscribeEvent;
import org.springframework.messaging.simp.annotation.SubscribeMapping;
import org.springframework.messaging.simp.annotation.support.PrincipalMethodArgumentResolver;
import org.springframework.messaging.simp.annotation.support.SendToMethodReturnValueHandler;
import org.springframework.messaging.simp.annotation.support.SubscriptionMethodReturnValueHandler;
@@ -66,7 +66,7 @@ import org.springframework.util.PathMatcher;
/**
* A handler for messages delegating to {@link SubscribeEvent @SubscribeEvent} and
* A handler for messages delegating to {@link org.springframework.messaging.simp.annotation.SubscribeMapping @SubscribeMapping} and
* {@link MessageMapping @MessageMapping} annotated methods.
* <p>
* Supports Ant-style path patterns as well as URI template variables in destinations.
@@ -214,22 +214,21 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
@Override
protected SimpMessageMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
MessageMapping messageMappingAnnot = AnnotationUtils.findAnnotation(method, MessageMapping.class);
if (messageMappingAnnot != null) {
SimpMessageMappingInfo result = createMessageMappingCondition(messageMappingAnnot);
MessageMapping typeAnnot = AnnotationUtils.findAnnotation(handlerType, MessageMapping.class);
MessageMapping typeAnnot = AnnotationUtils.findAnnotation(handlerType, MessageMapping.class);
MessageMapping messageAnnot = AnnotationUtils.findAnnotation(method, MessageMapping.class);
if (messageAnnot != null) {
SimpMessageMappingInfo result = createMessageMappingCondition(messageAnnot);
if (typeAnnot != null) {
result = createMessageMappingCondition(typeAnnot).combine(result);
}
return result;
}
SubscribeEvent subsribeAnnot = AnnotationUtils.findAnnotation(method, SubscribeEvent.class);
SubscribeMapping subsribeAnnot = AnnotationUtils.findAnnotation(method, SubscribeMapping.class);
if (subsribeAnnot != null) {
SimpMessageMappingInfo result = createSubscribeCondition(subsribeAnnot);
SubscribeEvent typeAnnot = AnnotationUtils.findAnnotation(handlerType, SubscribeEvent.class);
if (typeAnnot != null) {
result = createSubscribeCondition(typeAnnot).combine(result);
result = createMessageMappingCondition(typeAnnot).combine(result);
}
return result;
}
@@ -242,7 +241,7 @@ public class SimpAnnotationMethodMessageHandler extends AbstractMethodMessageHan
new DestinationPatternsMessageCondition(annotation.value()));
}
private SimpMessageMappingInfo createSubscribeCondition(SubscribeEvent annotation) {
private SimpMessageMappingInfo createSubscribeCondition(SubscribeMapping annotation) {
return new SimpMessageMappingInfo(SimpMessageTypeMessageCondition.SUBSCRIBE,
new DestinationPatternsMessageCondition(annotation.value()));
}

View File

@@ -24,7 +24,6 @@ import org.springframework.util.Assert;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
/**
* A message condition that checks the message type.
@@ -44,13 +43,6 @@ public class SimpMessageTypeMessageCondition extends AbstractMessageCondition<Si
private final SimpMessageType messageType;
/**
* A constructor accepting a message type.
*/
public SimpMessageTypeMessageCondition() {
this.messageType = null;
}
/**
* A constructor accepting a message type.
*
@@ -68,7 +60,7 @@ public class SimpMessageTypeMessageCondition extends AbstractMessageCondition<Si
@Override
protected Collection<?> getContent() {
return (this.messageType != null) ? Arrays.asList(messageType) : Collections.emptyList();
return Arrays.asList(messageType);
}
@Override
@@ -78,7 +70,7 @@ public class SimpMessageTypeMessageCondition extends AbstractMessageCondition<Si
@Override
public SimpMessageTypeMessageCondition combine(SimpMessageTypeMessageCondition other) {
return (this.messageType != null) ? this : other;
return other;
}
@Override
@@ -89,19 +81,22 @@ public class SimpMessageTypeMessageCondition extends AbstractMessageCondition<Si
return null;
}
return ((this.messageType != null) && this.messageType.equals(actualMessageType)) ? this : null;
return this;
}
@Override
public int compareTo(SimpMessageTypeMessageCondition other, Message<?> message) {
if ((this.messageType == null) && (other.messageType == null)) {
return 0;
}
if (this.messageType == null) {
return 1;
}
if (other.messageType == null) {
return -1;
Object actualMessageType = message.getHeaders().get(SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER);
if (actualMessageType != null) {
if (actualMessageType.equals(this.getMessageType()) && actualMessageType.equals(other.getMessageType())) {
return 0;
}
else if (actualMessageType.equals(this.getMessageType())) {
return -1;
}
else if (actualMessageType.equals(other.getMessageType())) {
return 1;
}
}
return 0;
}