Add @ReplyTo/@ReplyToUser, remove deps on spring-web

This commit is contained in:
Rossen Stoyanchev
2013-07-16 22:07:46 -04:00
parent 55dae74f15
commit 078cfb3e78
30 changed files with 1385 additions and 307 deletions

View File

@@ -32,10 +32,9 @@ import java.lang.annotation.Target;
@Documented
public @interface ReplyTo {
/**
* The destination value for the reply.
* The destination for a message created from the return value of a method.
*/
String value();
String[] value() default {};
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.handler.annotation.support;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.ExceptionDepthComparator;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.messaging.handler.method.HandlerMethodSelector;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
* Discovers annotated exception handling methods in a given class type, including all
* super types, and helps to resolve an Exception to a method that can handle it. The
* exception types supported by a given method can also be discovered from the method
* signature.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class ExceptionHandlerMethodResolver {
private static final Method NO_METHOD_FOUND = ClassUtils.getMethodIfAvailable(System.class, "currentTimeMillis");
private final Map<Class<? extends Throwable>, Method> mappedMethods =
new ConcurrentHashMap<Class<? extends Throwable>, Method>(16);
private final Map<Class<? extends Throwable>, Method> exceptionLookupCache =
new ConcurrentHashMap<Class<? extends Throwable>, Method>(16);
/**
* A constructor that finds {@link MessageExceptionHandler} methods in the given type.
* @param handlerType the type to introspect
*/
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
for (Method method : HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHOD_FILTER)) {
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
addExceptionMapping(exceptionType, method);
}
}
}
/**
* Extract exception mappings from the {@code @ExceptionHandler} annotation
* first and as a fall-back from the method signature.
*/
@SuppressWarnings("unchecked")
private List<Class<? extends Throwable>> detectExceptionMappings(Method method) {
List<Class<? extends Throwable>> result = new ArrayList<Class<? extends Throwable>>();
detectAnnotationExceptionMappings(method, result);
if (result.isEmpty()) {
for (Class<?> paramType : method.getParameterTypes()) {
if (Throwable.class.isAssignableFrom(paramType)) {
result.add((Class<? extends Throwable>) paramType);
}
}
}
Assert.notEmpty(result, "No exception types mapped to {" + method + "}");
return result;
}
protected void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
MessageExceptionHandler annot = AnnotationUtils.findAnnotation(method, MessageExceptionHandler.class);
result.addAll(Arrays.asList(annot.value()));
}
private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
Method oldMethod = this.mappedMethods.put(exceptionType, method);
if (oldMethod != null && !oldMethod.equals(method)) {
throw new IllegalStateException(
"Ambiguous @ExceptionHandler method mapped for [" + exceptionType + "]: {" +
oldMethod + ", " + method + "}.");
}
}
/**
* Whether the contained type has any exception mappings.
*/
public boolean hasExceptionMappings() {
return (this.mappedMethods.size() > 0);
}
/**
* Find a method to handle the given exception.
* Use {@link ExceptionDepthComparator} if more than one match is found.
* @param exception the exception
* @return a method to handle the exception or {@code null}
*/
public Method resolveMethod(Exception exception) {
Class<? extends Exception> exceptionType = exception.getClass();
Method method = this.exceptionLookupCache.get(exceptionType);
if (method == null) {
method = getMappedMethod(exceptionType);
this.exceptionLookupCache.put(exceptionType, method != null ? method : NO_METHOD_FOUND);
}
return method != NO_METHOD_FOUND ? method : null;
}
/**
* Return the method mapped to the given exception type or {@code null}.
*/
private Method getMappedMethod(Class<? extends Exception> exceptionType) {
List<Class<? extends Throwable>> matches = new ArrayList<Class<? extends Throwable>>();
for(Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
if (!matches.isEmpty()) {
Collections.sort(matches, new ExceptionDepthComparator(exceptionType));
return mappedMethods.get(matches.get(0));
}
else {
return null;
}
}
/** A filter for selecting annotated exception handling methods. */
public final static MethodFilter EXCEPTION_HANDLER_METHOD_FILTER = new MethodFilter() {
@Override
public boolean matches(Method method) {
return AnnotationUtils.findAnnotation(method, MessageExceptionHandler.class) != null;
}
};
}

View File

@@ -19,21 +19,26 @@ package org.springframework.messaging.handler.annotation.support;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.MessageBody;
import org.springframework.messaging.handler.method.MessageArgumentResolver;
import org.springframework.messaging.handler.method.HandlerMethodArgumentResolver;
import org.springframework.messaging.support.converter.MessageConverter;
import org.springframework.util.Assert;
/**
* TODO
*
* <p>This {@link HandlerMethodArgumentResolver} should be ordered last as it supports all
* types and does not require the {@link MessageBody} annotation.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class MessageBodyArgumentResolver implements MessageArgumentResolver {
public class MessageBodyMethodArgumentResolver implements HandlerMethodArgumentResolver {
private final MessageConverter<?> converter;
public MessageBodyArgumentResolver(MessageConverter<?> converter) {
public MessageBodyMethodArgumentResolver(MessageConverter<?> converter) {
Assert.notNull(converter, "converter is required");
this.converter = converter;
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.handler.annotation.support;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.method.annotation.ExceptionHandlerMethodResolver;
/**
* @author Rossen Stoyanchev
* @since 4.0
*/
public class MessageExceptionHandlerMethodResolver extends ExceptionHandlerMethodResolver {
public MessageExceptionHandlerMethodResolver(Class<?> handlerType) {
super(handlerType);
}
@Override
protected MethodFilter getExceptionHandlerMethods() {
return MESSAGE_EXCEPTION_HANDLER_METHODS;
}
@Override
protected void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
MessageExceptionHandler annotation = AnnotationUtils.findAnnotation(method, MessageExceptionHandler.class);
result.addAll(Arrays.asList(annotation.value()));
}
/**
* A filter for selecting {@code @ExceptionHandler} methods.
*/
public final static MethodFilter MESSAGE_EXCEPTION_HANDLER_METHODS = new MethodFilter() {
@Override
public boolean matches(Method method) {
return AnnotationUtils.findAnnotation(method, MessageExceptionHandler.class) != null;
}
};
}

View File

@@ -0,0 +1,285 @@
/*
* Copyright 2002-2012 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.handler.method;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Encapsulates information about a bean method consisting of a
* {@linkplain #getMethod() method} and a {@linkplain #getBean() bean}. Provides
* convenient access to method parameters, the method return value, method
* annotations.
*
* <p>The class may be created with a bean instance or with a bean name (e.g. lazy
* bean, prototype bean). Use {@link #createWithResolvedBean()} to obtain an
* {@link HandlerMethod} instance with a bean instance initialized through the
* bean factory.
*
* @author Arjen Poutsma
* @author Rossen Stoyanchev
* @since 4.0
*/
public class HandlerMethod {
/** Logger that is available to subclasses */
protected final Log logger = LogFactory.getLog(HandlerMethod.class);
private final Object bean;
private final Method method;
private final BeanFactory beanFactory;
private final MethodParameter[] parameters;
private final Method bridgedMethod;
/**
* Create an instance from a bean instance and a method.
*/
public HandlerMethod(Object bean, Method method) {
Assert.notNull(bean, "bean is required");
Assert.notNull(method, "method is required");
this.bean = bean;
this.beanFactory = null;
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters();
}
private MethodParameter[] initMethodParameters() {
int count = this.bridgedMethod.getParameterTypes().length;
MethodParameter[] result = new MethodParameter[count];
for (int i = 0; i < count; i++) {
result[i] = new HandlerMethodParameter(i);
}
return result;
}
/**
* Create an instance from a bean instance, method name, and parameter types.
* @throws NoSuchMethodException when the method cannot be found
*/
public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
Assert.notNull(bean, "bean is required");
Assert.notNull(methodName, "method is required");
this.bean = bean;
this.beanFactory = null;
this.method = bean.getClass().getMethod(methodName, parameterTypes);
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters();
}
/**
* Create an instance from a bean name, a method, and a {@code BeanFactory}.
* The method {@link #createWithResolvedBean()} may be used later to
* re-create the {@code HandlerMethod} with an initialized the bean.
*/
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
Assert.hasText(beanName, "beanName is required");
Assert.notNull(beanFactory, "beanFactory is required");
Assert.notNull(method, "method is required");
Assert.isTrue(beanFactory.containsBean(beanName),
"Bean factory [" + beanFactory + "] does not contain bean [" + beanName + "]");
this.bean = beanName;
this.beanFactory = beanFactory;
this.method = method;
this.bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
this.parameters = initMethodParameters();
}
/**
* Copy constructor for use in sub-classes.
*/
protected HandlerMethod(HandlerMethod handlerMethod) {
Assert.notNull(handlerMethod, "HandlerMethod is required");
this.bean = handlerMethod.bean;
this.beanFactory = handlerMethod.beanFactory;
this.method = handlerMethod.method;
this.bridgedMethod = handlerMethod.bridgedMethod;
this.parameters = handlerMethod.parameters;
}
/**
* Re-create HandlerMethod with the resolved handler.
*/
private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
Assert.notNull(handlerMethod, "handlerMethod is required");
Assert.notNull(handler, "handler is required");
this.bean = handler;
this.beanFactory = handlerMethod.beanFactory;
this.method = handlerMethod.method;
this.bridgedMethod = handlerMethod.bridgedMethod;
this.parameters = handlerMethod.parameters;
}
/**
* Returns the bean for this handler method.
*/
public Object getBean() {
return this.bean;
}
/**
* Returns the method for this handler method.
*/
public Method getMethod() {
return this.method;
}
/**
* Returns the type of the handler for this handler method.
* Note that if the bean type is a CGLIB-generated class, the original, user-defined class is returned.
*/
public Class<?> getBeanType() {
Class<?> clazz = (this.bean instanceof String)
? this.beanFactory.getType((String) this.bean) : this.bean.getClass();
return ClassUtils.getUserClass(clazz);
}
/**
* If the bean method is a bridge method, this method returns the bridged (user-defined) method.
* Otherwise it returns the same method as {@link #getMethod()}.
*/
protected Method getBridgedMethod() {
return this.bridgedMethod;
}
/**
* Returns the method parameters for this handler method.
*/
public MethodParameter[] getMethodParameters() {
return this.parameters;
}
/**
* Return the HandlerMethod return type.
*/
public MethodParameter getReturnType() {
return new HandlerMethodParameter(-1);
}
/**
* Return the actual return value type.
*/
public MethodParameter getReturnValueType(Object returnValue) {
return new ReturnValueMethodParameter(returnValue);
}
/**
* Returns {@code true} if the method return type is void, {@code false} otherwise.
*/
public boolean isVoid() {
return Void.TYPE.equals(getReturnType().getParameterType());
}
/**
* Returns a single annotation on the underlying method traversing its super methods if no
* annotation can be found on the given method itself.
* @param annotationType the type of annotation to introspect the method for.
* @return the annotation, or {@code null} if none found
*/
public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
return AnnotationUtils.findAnnotation(this.bridgedMethod, annotationType);
}
/**
* If the provided instance contains a bean name rather than an object instance, the bean name is resolved
* before a {@link HandlerMethod} is created and returned.
*/
public HandlerMethod createWithResolvedBean() {
Object handler = this.bean;
if (this.bean instanceof String) {
String beanName = (String) this.bean;
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o != null && o instanceof HandlerMethod) {
HandlerMethod other = (HandlerMethod) o;
return this.bean.equals(other.bean) && this.method.equals(other.method);
}
return false;
}
@Override
public int hashCode() {
return 31 * this.bean.hashCode() + this.method.hashCode();
}
@Override
public String toString() {
return method.toGenericString();
}
/**
* A MethodParameter with HandlerMethod-specific behavior.
*/
private class HandlerMethodParameter extends MethodParameter {
protected HandlerMethodParameter(int index) {
super(HandlerMethod.this.bridgedMethod, index);
}
@Override
public Class<?> getDeclaringClass() {
return HandlerMethod.this.getBeanType();
}
@Override
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
return HandlerMethod.this.getMethodAnnotation(annotationType);
}
}
/**
* A MethodParameter for a HandlerMethod return type based on an actual return value.
*/
private class ReturnValueMethodParameter extends HandlerMethodParameter {
private final Object returnValue;
public ReturnValueMethodParameter(Object returnValue) {
super(-1);
this.returnValue = returnValue;
}
@Override
public Class<?> getParameterType() {
return (this.returnValue != null) ? this.returnValue.getClass() : super.getParameterType();
}
}
}

View File

@@ -27,7 +27,7 @@ import org.springframework.messaging.Message;
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface MessageArgumentResolver {
public interface HandlerMethodArgumentResolver {
/**
* Whether the given {@linkplain MethodParameter method parameter} is

View File

@@ -30,32 +30,32 @@ import org.springframework.util.Assert;
/**
* Resolves method parameters by delegating to a list of registered
* {@link MessageArgumentResolver}. Previously resolved method parameters are cached
* {@link HandlerMethodArgumentResolver}. Previously resolved method parameters are cached
* for faster lookups.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class MessageArgumentResolverComposite implements MessageArgumentResolver {
public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
protected final Log logger = LogFactory.getLog(getClass());
private final List<MessageArgumentResolver> argumentResolvers = new LinkedList<MessageArgumentResolver>();
private final List<HandlerMethodArgumentResolver> argumentResolvers = new LinkedList<HandlerMethodArgumentResolver>();
private final Map<MethodParameter, MessageArgumentResolver> argumentResolverCache =
new ConcurrentHashMap<MethodParameter, MessageArgumentResolver>(256);
private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache =
new ConcurrentHashMap<MethodParameter, HandlerMethodArgumentResolver>(256);
/**
* Return a read-only list with the contained resolvers, or an empty list.
*/
public List<MessageArgumentResolver> getResolvers() {
public List<HandlerMethodArgumentResolver> getResolvers() {
return Collections.unmodifiableList(this.argumentResolvers);
}
/**
* Whether the given {@linkplain MethodParameter method parameter} is supported by any registered
* {@link MessageArgumentResolver}.
* {@link HandlerMethodArgumentResolver}.
*/
@Override
public boolean supportsParameter(MethodParameter parameter) {
@@ -63,24 +63,24 @@ public class MessageArgumentResolverComposite implements MessageArgumentResolver
}
/**
* Iterate over registered {@link MessageArgumentResolver}s and invoke the one that supports it.
* @exception IllegalStateException if no suitable {@link MessageArgumentResolver} is found.
* Iterate over registered {@link HandlerMethodArgumentResolver}s and invoke the one that supports it.
* @exception IllegalStateException if no suitable {@link HandlerMethodArgumentResolver} is found.
*/
@Override
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
MessageArgumentResolver resolver = getArgumentResolver(parameter);
HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
Assert.notNull(resolver, "Unknown parameter type [" + parameter.getParameterType().getName() + "]");
return resolver.resolveArgument(parameter, message);
}
/**
* Find a registered {@link MessageArgumentResolver} that supports the given method parameter.
* Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter.
*/
private MessageArgumentResolver getArgumentResolver(MethodParameter parameter) {
MessageArgumentResolver result = this.argumentResolverCache.get(parameter);
private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
if (result == null) {
for (MessageArgumentResolver resolver : this.argumentResolvers) {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
result = resolver;
this.argumentResolverCache.put(parameter, result);
@@ -92,19 +92,19 @@ public class MessageArgumentResolverComposite implements MessageArgumentResolver
}
/**
* Add the given {@link MessageArgumentResolver}.
* Add the given {@link HandlerMethodArgumentResolver}.
*/
public MessageArgumentResolverComposite addResolver(MessageArgumentResolver argumentResolver) {
public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver argumentResolver) {
this.argumentResolvers.add(argumentResolver);
return this;
}
/**
* Add the given {@link MessageArgumentResolver}s.
* Add the given {@link HandlerMethodArgumentResolver}s.
*/
public MessageArgumentResolverComposite addResolvers(List<? extends MessageArgumentResolver> argumentResolvers) {
public HandlerMethodArgumentResolverComposite addResolvers(List<? extends HandlerMethodArgumentResolver> argumentResolvers) {
if (argumentResolvers != null) {
for (MessageArgumentResolver resolver : argumentResolvers) {
for (HandlerMethodArgumentResolver resolver : argumentResolvers) {
this.argumentResolvers.add(resolver);
}
}

View File

@@ -27,7 +27,7 @@ import org.springframework.messaging.Message;
* @author Rossen Stoyanchev
* @since 4.0
*/
public interface MessageReturnValueHandler {
public interface HandlerMethodReturnValueHandler {
/**
* Whether the given {@linkplain MethodParameter method return type} is

View File

@@ -28,25 +28,25 @@ import org.springframework.util.Assert;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class MessageReturnValueHandlerComposite implements MessageReturnValueHandler {
public class HandlerMethodReturnValueHandlerComposite implements HandlerMethodReturnValueHandler {
private final List<MessageReturnValueHandler> returnValueHandlers = new ArrayList<MessageReturnValueHandler>();
private final List<HandlerMethodReturnValueHandler> returnValueHandlers = new ArrayList<HandlerMethodReturnValueHandler>();
/**
* Add the given {@link MessageReturnValueHandler}.
* Add the given {@link HandlerMethodReturnValueHandler}.
*/
public MessageReturnValueHandlerComposite addHandler(MessageReturnValueHandler returnValuehandler) {
public HandlerMethodReturnValueHandlerComposite addHandler(HandlerMethodReturnValueHandler returnValuehandler) {
this.returnValueHandlers.add(returnValuehandler);
return this;
}
/**
* Add the given {@link MessageReturnValueHandler}s.
* Add the given {@link HandlerMethodReturnValueHandler}s.
*/
public MessageReturnValueHandlerComposite addHandlers(List<? extends MessageReturnValueHandler> handlers) {
public HandlerMethodReturnValueHandlerComposite addHandlers(List<? extends HandlerMethodReturnValueHandler> handlers) {
if (handlers != null) {
for (MessageReturnValueHandler handler : handlers) {
for (HandlerMethodReturnValueHandler handler : handlers) {
this.returnValueHandlers.add(handler);
}
}
@@ -58,8 +58,8 @@ public class MessageReturnValueHandlerComposite implements MessageReturnValueHan
return getReturnValueHandler(returnType) != null;
}
private MessageReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
for (MessageReturnValueHandler handler : this.returnValueHandlers) {
private HandlerMethodReturnValueHandler getReturnValueHandler(MethodParameter returnType) {
for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
if (handler.supportsReturnType(returnType)) {
return handler;
}
@@ -71,7 +71,7 @@ public class MessageReturnValueHandlerComposite implements MessageReturnValueHan
public void handleReturnValue(Object returnValue, MethodParameter returnType, Message<?> message)
throws Exception {
MessageReturnValueHandler handler = getReturnValueHandler(returnType);
HandlerMethodReturnValueHandler handler = getReturnValueHandler(returnType);
Assert.notNull(handler, "Unknown return value type [" + returnType.getParameterType().getName() + "]");
handler.handleReturnValue(returnValue, returnType, message);
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.handler.method;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
/**
* Defines the algorithm for searching handler methods exhaustively including interfaces and parent
* classes while also dealing with parameterized methods as well as interface and class-based proxies.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract class HandlerMethodSelector {
/**
* Selects handler methods for the given handler type. Callers of this method define handler methods
* of interest through the {@link MethodFilter} parameter.
*
* @param handlerType the handler type to search handler methods on
* @param handlerMethodFilter a {@link MethodFilter} to help recognize handler methods of interest
* @return the selected methods, or an empty set
*/
public static Set<Method> selectMethods(final Class<?> handlerType, final MethodFilter handlerMethodFilter) {
final Set<Method> handlerMethods = new LinkedHashSet<Method>();
Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
Class<?> specificHandlerType = null;
if (!Proxy.isProxyClass(handlerType)) {
handlerTypes.add(handlerType);
specificHandlerType = handlerType;
}
handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
for (Class<?> currentHandlerType : handlerTypes) {
final Class<?> targetClass = (specificHandlerType != null ? specificHandlerType : currentHandlerType);
ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
@Override
public void doWith(Method method) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (handlerMethodFilter.matches(specificMethod) &&
(bridgedMethod == specificMethod || !handlerMethodFilter.matches(bridgedMethod))) {
handlerMethods.add(specificMethod);
}
}
}, ReflectionUtils.USER_DECLARED_METHODS);
}
return handlerMethods;
}
}

View File

@@ -26,21 +26,20 @@ import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.messaging.Message;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.method.HandlerMethod;
/**
* Invokes the handler method for a given message after resolving
* its method argument values through registered {@link MessageArgumentResolver}s.
* its method argument values through registered {@link HandlerMethodArgumentResolver}s.
* <p>
* Use {@link #setMessageMethodArgumentResolvers(MessageArgumentResolverComposite)}
* Use {@link #setMessageMethodArgumentResolvers(HandlerMethodArgumentResolverComposite)}
* to customize the list of argument resolvers.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class InvocableMessageHandlerMethod extends HandlerMethod {
public class InvocableHandlerMethod extends HandlerMethod {
private MessageArgumentResolverComposite argumentResolvers = new MessageArgumentResolverComposite();
private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
@@ -48,14 +47,14 @@ public class InvocableMessageHandlerMethod extends HandlerMethod {
/**
* Create an instance from a {@code HandlerMethod}.
*/
public InvocableMessageHandlerMethod(HandlerMethod handlerMethod) {
public InvocableHandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod);
}
/**
* Create an instance from a bean instance and a method.
*/
public InvocableMessageHandlerMethod(Object bean, Method method) {
public InvocableHandlerMethod(Object bean, Method method) {
super(bean, method);
}
@@ -68,17 +67,17 @@ public class InvocableMessageHandlerMethod extends HandlerMethod {
* @param parameterTypes the method parameter types
* @throws NoSuchMethodException when the method cannot be found
*/
public InvocableMessageHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
public InvocableHandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
throws NoSuchMethodException {
super(bean, methodName, parameterTypes);
}
/**
* Set {@link MessageArgumentResolver}s to use to use for resolving method
* Set {@link HandlerMethodArgumentResolver}s to use to use for resolving method
* argument values.
*/
public void setMessageMethodArgumentResolvers(MessageArgumentResolverComposite argumentResolvers) {
public void setMessageMethodArgumentResolvers(HandlerMethodArgumentResolverComposite argumentResolvers) {
this.argumentResolvers = argumentResolvers;
}

View File

@@ -47,6 +47,7 @@ public class SimpMessageHeaderAccessor extends NativeMessageHeaderAccessor {
public static final String MESSAGE_TYPE = "messageType";
// TODO
public static final String PROTOCOL_MESSAGE_TYPE = "protocolMessageType";
public static final String SESSION_ID = "sessionId";

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.messaging.simp;
import java.util.Arrays;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
@@ -75,11 +77,9 @@ public class SimpMessagingTemplate extends AbstractMessageSendingTemplate<String
protected <P> Message<P> addDestinationToMessage(Message<P> message, String destination) {
Assert.notNull(destination, "destination is required");
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headers.copyHeaders(message.getHeaders());
headers.setDestination(destination);
message = MessageBuilder.withPayload(message.getPayload()).copyHeaders(headers.toMap()).build();
return message;
return MessageBuilder.fromMessage(message)
.setHeader(SimpMessageHeaderAccessor.MESSAGE_TYPE, SimpMessageType.MESSAGE)
.setHeader(SimpMessageHeaderAccessor.DESTINATIONS, Arrays.asList(destination)).build();
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.simp.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Rossen Stoyanchev
* @since 4.0
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ReplyToUser {
/**
* The destination for a message based on the return value of a method.
*/
String[] value() default {};
}

View File

@@ -0,0 +1,5 @@
/**
* Annotations and support classes for handling messages from simple messaging
* protocols (like STOMP).
*/
package org.springframework.messaging.simp.annotation;

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.simp.annotation.support;
import java.security.Principal;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.handler.annotation.ReplyTo;
import org.springframework.messaging.handler.method.MessageReturnValueHandler;
import org.springframework.messaging.handler.method.MissingSessionUserException;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.converter.MessageConverter;
import org.springframework.util.Assert;
/**
* Expects return values to be either a {@link Message} or the payload of a message to be
* converted and sent on a {@link MessageChannel}.
*
* <p>This {@link MessageReturnValueHandler} should be ordered last as it supports all
* return value types.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class DefaultMessageReturnValueHandler implements MessageReturnValueHandler {
private MessageChannel inboundChannel;
private MessageChannel outboundChannel;
private final MessageConverter converter;
public DefaultMessageReturnValueHandler(MessageChannel inboundChannel, MessageChannel outboundChannel,
MessageConverter<?> converter) {
Assert.notNull(inboundChannel, "inboundChannel is required");
Assert.notNull(outboundChannel, "outboundChannel is required");
Assert.notNull(converter, "converter is required");
this.inboundChannel = inboundChannel;
this.outboundChannel = outboundChannel;
this.converter = converter;
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return true;
}
@SuppressWarnings("unchecked")
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, Message<?> message)
throws Exception {
if (returnValue == null) {
return;
}
SimpMessageHeaderAccessor inputHeaders = SimpMessageHeaderAccessor.wrap(message);
Message<?> returnMessage = (returnValue instanceof Message) ? (Message<?>) returnValue : null;
Object returnPayload = (returnMessage != null) ? returnMessage.getPayload() : returnValue;
SimpMessageHeaderAccessor returnHeaders = (returnMessage != null) ?
SimpMessageHeaderAccessor.wrap(returnMessage) : SimpMessageHeaderAccessor.create();
returnHeaders.setSessionId(inputHeaders.getSessionId());
returnHeaders.setSubscriptionId(inputHeaders.getSubscriptionId());
String destination = getDestination(message, returnType, inputHeaders, returnHeaders);
returnHeaders.setDestination(destination);
returnMessage = this.converter.toMessage(returnPayload);
returnMessage = MessageBuilder.fromMessage(returnMessage).copyHeaders(returnHeaders.toMap()).build();
if (destination.startsWith("/user/")) {
this.inboundChannel.send(returnMessage);
}
else {
this.outboundChannel.send(returnMessage);
}
}
protected String getDestination(Message<?> inputMessage, MethodParameter returnType,
SimpMessageHeaderAccessor inputHeaders, SimpMessageHeaderAccessor returnHeaders) {
ReplyTo annot = returnType.getMethodAnnotation(ReplyTo.class);
if (returnHeaders.getDestination() != null) {
return returnHeaders.getDestination();
}
else if (annot != null) {
Principal user = inputHeaders.getUser();
if (user == null) {
throw new MissingSessionUserException(inputMessage);
}
return "/user/" + user.getName() + annot.value();
}
else if (inputHeaders.getDestination() != null) {
return inputHeaders.getDestination();
}
else {
return null;
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.messaging.handler.method;
package org.springframework.messaging.simp.annotation.support;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessagingException;

View File

@@ -20,8 +20,7 @@ import java.security.Principal;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.method.MessageArgumentResolver;
import org.springframework.messaging.handler.method.MissingSessionUserException;
import org.springframework.messaging.handler.method.HandlerMethodArgumentResolver;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
@@ -29,7 +28,7 @@ import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class PrincipalMessageArgumentResolver implements MessageArgumentResolver {
public class PrincipalMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.simp.annotation.support;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.core.MessagePostProcessor;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.handler.annotation.ReplyTo;
import org.springframework.messaging.handler.method.HandlerMethodReturnValueHandler;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.annotation.ReplyToUser;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.util.Assert;
/**
* A {@link HandlerMethodReturnValueHandler} for replying to destinations specified in a
* {@link ReplyTo} or {@link ReplyToUser} method-level annotations.
* <p>
* The value returned from the method is converted, and turned to a {@link Message} and
* sent through the provided {@link MessageChannel}. The
* message is then enriched with the sessionId of the input message as well as the
* destination from the annotation(s). If multiple destinations are specified, a copy of
* the message is sent to each destination.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class ReplyToMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
private final MessageSendingOperations<String> messagingTemplate;
public ReplyToMethodReturnValueHandler(MessageSendingOperations<String> messagingTemplate) {
Assert.notNull(messagingTemplate, "messagingTemplate is required");
this.messagingTemplate = messagingTemplate;
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ((returnType.getMethodAnnotation(ReplyTo.class) != null)
|| (returnType.getMethodAnnotation(ReplyToUser.class) != null));
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, Message<?> inputMessage)
throws Exception {
if (returnValue == null) {
return;
}
ReplyTo replyTo = returnType.getMethodAnnotation(ReplyTo.class);
ReplyToUser replyToUser = returnType.getMethodAnnotation(ReplyToUser.class);
List<String> destinations = new ArrayList<String>();
if (replyTo != null) {
destinations.addAll(Arrays.asList(replyTo.value()));
}
if (replyToUser != null) {
Principal user = getUser(inputMessage);
for (String destination : replyToUser.value()) {
destinations.add("/user/" + user.getName() + destination);
}
}
MessagePostProcessor postProcessor = new SessionIdHeaderPostProcessor(inputMessage);
for (String destination : destinations) {
this.messagingTemplate.convertAndSend(destination, returnValue, postProcessor);
}
}
private Principal getUser(Message<?> inputMessage) {
SimpMessageHeaderAccessor inputHeaders = SimpMessageHeaderAccessor.wrap(inputMessage);
Principal user = inputHeaders.getUser();
if (user == null) {
throw new MissingSessionUserException(inputMessage);
}
return user;
}
private final class SessionIdHeaderPostProcessor implements MessagePostProcessor {
private final Message<?> inputMessage;
public SessionIdHeaderPostProcessor(Message<?> inputMessage) {
this.inputMessage = inputMessage;
}
@Override
public Message<?> postProcessMessage(Message<?> message) {
String headerName = SimpMessageHeaderAccessor.SESSION_ID;
String sessionId = (String) this.inputMessage.getHeaders().get(headerName);
return MessageBuilder.fromMessage(message).setHeader(headerName, sessionId).build();
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2002-2013 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.messaging.simp.annotation.support;
import org.springframework.core.MethodParameter;
import org.springframework.messaging.Message;
import org.springframework.messaging.core.MessagePostProcessor;
import org.springframework.messaging.core.MessageSendingOperations;
import org.springframework.messaging.handler.annotation.ReplyTo;
import org.springframework.messaging.handler.method.HandlerMethodReturnValueHandler;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.annotation.ReplyToUser;
import org.springframework.messaging.simp.annotation.SubscribeEvent;
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} that do not also annotated with
* neither {@link ReplyTo} nor {@link ReplyToUser}.
*
* <p>The value returned from the method is converted, and turned to a {@link Message} and
* then enriched with the sessionId, subscriptionId, and destination of the input message.
* The message is then sent directly back to the connected client.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public class SubscriptionMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
private final MessageSendingOperations<String> messagingTemplate;
public SubscriptionMethodReturnValueHandler(MessageSendingOperations<String> messagingTemplate) {
Assert.notNull(messagingTemplate, "messagingTemplate is required");
this.messagingTemplate = messagingTemplate;
}
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return ((returnType.getMethodAnnotation(SubscribeEvent.class) != null)
&& (returnType.getMethodAnnotation(ReplyTo.class) == null)
&& (returnType.getMethodAnnotation(ReplyToUser.class) == null));
}
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, Message<?> message)
throws Exception {
if (returnValue == null) {
return;
}
SimpMessageHeaderAccessor inputHeaders = SimpMessageHeaderAccessor.wrap(message);
String destination = inputHeaders.getDestination();
Assert.state(inputHeaders.getSubscriptionId() != null,
"No subsriptiondId in input message. Add @ReplyTo or @ReplyToUser to method: "
+ returnType.getMethod());
MessagePostProcessor postProcessor = new InputHeaderCopyingPostProcessor(inputHeaders);
this.messagingTemplate.convertAndSend(destination, returnValue, postProcessor);
}
private final class InputHeaderCopyingPostProcessor implements MessagePostProcessor {
private final SimpMessageHeaderAccessor inputHeaders;
public InputHeaderCopyingPostProcessor(SimpMessageHeaderAccessor inputHeaders) {
this.inputHeaders = inputHeaders;
}
@Override
public Message<?> postProcessMessage(Message<?> message) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
return MessageBuilder.fromMessage(message)
.setHeader(SimpMessageHeaderAccessor.SESSION_ID, this.inputHeaders.getSessionId())
.setHeader(SimpMessageHeaderAccessor.SUBSCRIPTION_ID, this.inputHeaders.getSubscriptionId())
.copyHeaders(headers.toMap()).build();
}
}
}

View File

@@ -38,24 +38,26 @@ import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.support.MessageBodyArgumentResolver;
import org.springframework.messaging.handler.annotation.support.MessageExceptionHandlerMethodResolver;
import org.springframework.messaging.handler.method.InvocableMessageHandlerMethod;
import org.springframework.messaging.handler.method.MessageArgumentResolverComposite;
import org.springframework.messaging.handler.method.MessageReturnValueHandlerComposite;
import org.springframework.messaging.handler.annotation.support.ExceptionHandlerMethodResolver;
import org.springframework.messaging.handler.annotation.support.MessageBodyMethodArgumentResolver;
import org.springframework.messaging.handler.method.HandlerMethod;
import org.springframework.messaging.handler.method.HandlerMethodArgumentResolverComposite;
import org.springframework.messaging.handler.method.HandlerMethodReturnValueHandlerComposite;
import org.springframework.messaging.handler.method.HandlerMethodSelector;
import org.springframework.messaging.handler.method.InvocableHandlerMethod;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SubscribeEvent;
import org.springframework.messaging.simp.annotation.UnsubscribeEvent;
import org.springframework.messaging.simp.annotation.support.DefaultMessageReturnValueHandler;
import org.springframework.messaging.simp.annotation.support.PrincipalMessageArgumentResolver;
import org.springframework.messaging.simp.annotation.support.PrincipalMethodArgumentResolver;
import org.springframework.messaging.simp.annotation.support.ReplyToMethodReturnValueHandler;
import org.springframework.messaging.simp.annotation.support.SubscriptionMethodReturnValueHandler;
import org.springframework.messaging.support.converter.MessageConverter;
import org.springframework.stereotype.Controller;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils.MethodFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
/**
@@ -80,12 +82,12 @@ public class AnnotationMethodMessageHandler implements MessageHandler, Applicati
private Map<MappingInfo, HandlerMethod> unsubscribeMethods = new HashMap<MappingInfo, HandlerMethod>();
private final Map<Class<?>, MessageExceptionHandlerMethodResolver> exceptionHandlerCache =
new ConcurrentHashMap<Class<?>, MessageExceptionHandlerMethodResolver>(64);
private final Map<Class<?>, ExceptionHandlerMethodResolver> exceptionHandlerCache =
new ConcurrentHashMap<Class<?>, ExceptionHandlerMethodResolver>(64);
private MessageArgumentResolverComposite argumentResolvers = new MessageArgumentResolverComposite();
private HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
private MessageReturnValueHandlerComposite returnValueHandlers = new MessageReturnValueHandlerComposite();
private HandlerMethodReturnValueHandlerComposite returnValueHandlers = new HandlerMethodReturnValueHandlerComposite();
/**
@@ -116,11 +118,17 @@ public class AnnotationMethodMessageHandler implements MessageHandler, Applicati
initHandlerMethods();
this.argumentResolvers.addResolver(new PrincipalMessageArgumentResolver());
this.argumentResolvers.addResolver(new MessageBodyArgumentResolver(this.messageConverter));
this.argumentResolvers.addResolver(new PrincipalMethodArgumentResolver());
this.argumentResolvers.addResolver(new MessageBodyMethodArgumentResolver(this.messageConverter));
this.returnValueHandlers.addHandler(new DefaultMessageReturnValueHandler(
this.inboundChannel, this.outboundChannel, this.messageConverter));
SimpMessagingTemplate inboundMessagingTemplate = new SimpMessagingTemplate(this.inboundChannel);
inboundMessagingTemplate.setConverter(this.messageConverter);
SimpMessagingTemplate outboundMessagingTemplate = new SimpMessagingTemplate(this.outboundChannel);
outboundMessagingTemplate.setConverter(this.messageConverter);
this.returnValueHandlers.addHandler(new ReplyToMethodReturnValueHandler(inboundMessagingTemplate));
this.returnValueHandlers.addHandler(new SubscriptionMethodReturnValueHandler(outboundMessagingTemplate));
}
protected void initHandlerMethods() {
@@ -213,8 +221,7 @@ public class AnnotationMethodMessageHandler implements MessageHandler, Applicati
HandlerMethod handlerMethod = match.createWithResolvedBean();
// TODO: avoid re-creating invocableHandlerMethod
InvocableMessageHandlerMethod invocableHandlerMethod = new InvocableMessageHandlerMethod(handlerMethod);
InvocableHandlerMethod invocableHandlerMethod = new InvocableHandlerMethod(handlerMethod);
invocableHandlerMethod.setMessageMethodArgumentResolvers(this.argumentResolvers);
try {
@@ -237,11 +244,11 @@ public class AnnotationMethodMessageHandler implements MessageHandler, Applicati
private void invokeExceptionHandler(Message<?> message, HandlerMethod handlerMethod, Exception ex) {
InvocableMessageHandlerMethod exceptionHandlerMethod;
InvocableHandlerMethod exceptionHandlerMethod;
Class<?> beanType = handlerMethod.getBeanType();
MessageExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(beanType);
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(beanType);
if (resolver == null) {
resolver = new MessageExceptionHandlerMethodResolver(beanType);
resolver = new ExceptionHandlerMethodResolver(beanType);
this.exceptionHandlerCache.put(beanType, resolver);
}
@@ -251,7 +258,7 @@ public class AnnotationMethodMessageHandler implements MessageHandler, Applicati
return;
}
exceptionHandlerMethod = new InvocableMessageHandlerMethod(handlerMethod.getBean(), method);
exceptionHandlerMethod = new InvocableHandlerMethod(handlerMethod.getBean(), method);
exceptionHandlerMethod.setMessageMethodArgumentResolvers(this.argumentResolvers);
try {

View File

@@ -34,7 +34,7 @@ import org.springframework.util.StringUtils;
* Supports destinations prefixed with "/user/{username}" and resolves them into a
* destination to which the user is currently subscribed by appending the user session id.
* For example a destination such as "/user/john/queue/trade-confirmation" would resolve
* to "/trade-confirmation/i9oqdfzo" if "i9oqdfzo" is the user's session id.
* to "/queue/trade-confirmation/i9oqdfzo" if "i9oqdfzo" is the user's session id.
*
* @author Rossen Stoyanchev
* @since 4.0

View File

@@ -18,8 +18,8 @@ package org.springframework.messaging.support.channel;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.SubscribableChannel;
@@ -31,28 +31,28 @@ import org.springframework.messaging.SubscribableChannel;
* @author Rossen Stoyanchev
* @since 4.0
*/
public class TaskExecutorSubscribableChannel extends AbstractSubscribableChannel {
public class ExecutorSubscribableChannel extends AbstractSubscribableChannel {
private final TaskExecutor executor;
private final Executor executor;
private final Set<MessageHandler> handlers = new CopyOnWriteArraySet<MessageHandler>();
/**
* Create a new {@link TaskExecutorSubscribableChannel} instance where messages will be sent
* Create a new {@link ExecutorSubscribableChannel} instance where messages will be sent
* in the callers thread.
*/
public TaskExecutorSubscribableChannel() {
public ExecutorSubscribableChannel() {
this(null);
}
/**
* Create a new {@link TaskExecutorSubscribableChannel} instance where messages will be sent
* Create a new {@link ExecutorSubscribableChannel} instance where messages will be sent
* via the specified executor.
* @param executor the executor used to send the message or {@code null} to execute in
* the callers thread.
*/
public TaskExecutorSubscribableChannel(TaskExecutor executor) {
public ExecutorSubscribableChannel(Executor executor) {
this.executor = executor;
}