Add @ReplyTo/@ReplyToUser, remove deps on spring-web
This commit is contained in:
parent
55dae74f15
commit
078cfb3e78
|
|
@ -320,7 +320,6 @@ project("spring-messaging") {
|
|||
compile(project(":spring-beans"))
|
||||
compile(project(":spring-core"))
|
||||
compile(project(":spring-context"))
|
||||
optional(project(":spring-web")) // TODO: MediaType/HandlerMethod/EHMR
|
||||
optional(project(":spring-websocket"))
|
||||
optional("com.fasterxml.jackson.core:jackson-databind:2.2.0")
|
||||
optional("org.projectreactor:reactor-core:1.0.0.BUILD-SNAPSHOT")
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* Annotations and support classes for handling messages from simple messaging
|
||||
* protocols (like STOMP).
|
||||
*/
|
||||
package org.springframework.messaging.simp.annotation;
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* 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.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.BindException;
|
||||
import java.net.SocketException;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ExceptionHandlerMethodResolver} tests.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ExceptionHandlerMethodResolverTests {
|
||||
|
||||
@Test
|
||||
public void resolveMethodFromAnnotation() {
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
|
||||
IOException exception = new IOException();
|
||||
assertEquals("handleIOException", resolver.resolveMethod(exception).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMethodFromArgument() {
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
|
||||
IllegalArgumentException exception = new IllegalArgumentException();
|
||||
assertEquals("handleIllegalArgumentException", resolver.resolveMethod(exception).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMethodExceptionSubType() {
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
|
||||
IOException ioException = new FileNotFoundException();
|
||||
assertEquals("handleIOException", resolver.resolveMethod(ioException).getName());
|
||||
SocketException bindException = new BindException();
|
||||
assertEquals("handleSocketException", resolver.resolveMethod(bindException).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMethodBestMatch() {
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
|
||||
SocketException exception = new SocketException();
|
||||
assertEquals("handleSocketException", resolver.resolveMethod(exception).getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMethodNoMatch() {
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(ExceptionController.class);
|
||||
Exception exception = new Exception();
|
||||
assertNull("1st lookup", resolver.resolveMethod(exception));
|
||||
assertNull("2nd lookup from cache", resolver.resolveMethod(exception));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveMethodInherited() {
|
||||
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(InheritedController.class);
|
||||
IOException exception = new IOException();
|
||||
assertEquals("handleIOException", resolver.resolveMethod(exception).getName());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void ambiguousExceptionMapping() {
|
||||
new ExceptionHandlerMethodResolver(AmbiguousController.class);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void noExceptionMapping() {
|
||||
new ExceptionHandlerMethodResolver(NoExceptionController.class);
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class ExceptionController {
|
||||
|
||||
public void handle() {}
|
||||
|
||||
@MessageExceptionHandler(IOException.class)
|
||||
public void handleIOException() {
|
||||
}
|
||||
|
||||
@MessageExceptionHandler(SocketException.class)
|
||||
public void handleSocketException() {
|
||||
}
|
||||
|
||||
@MessageExceptionHandler
|
||||
public void handleIllegalArgumentException(IllegalArgumentException exception) {
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class InheritedController extends ExceptionController {
|
||||
|
||||
@Override
|
||||
public void handleIOException() {
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class AmbiguousController {
|
||||
|
||||
public void handle() {}
|
||||
|
||||
@MessageExceptionHandler({BindException.class, IllegalArgumentException.class})
|
||||
public String handle1(Exception ex) throws IOException {
|
||||
return ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
|
||||
@MessageExceptionHandler
|
||||
public String handle2(IllegalArgumentException ex) {
|
||||
return ClassUtils.getShortName(ex.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
@Controller
|
||||
static class NoExceptionController {
|
||||
|
||||
@MessageExceptionHandler
|
||||
public void handle() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* 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.lang.reflect.Method;
|
||||
import java.security.Principal;
|
||||
|
||||
import javax.security.auth.Subject;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.handler.annotation.ReplyTo;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.messaging.simp.annotation.ReplyToUser;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.converter.MessageConverter;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
|
||||
/**
|
||||
* Test fixture for {@link ReplyToMethodReturnValueHandlerTests}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class ReplyToMethodReturnValueHandlerTests {
|
||||
|
||||
private static final String payloadContent = "payload";
|
||||
|
||||
|
||||
private ReplyToMethodReturnValueHandler handler;
|
||||
|
||||
@Mock private MessageChannel messageChannel;
|
||||
|
||||
@Captor ArgumentCaptor<Message<?>> messageCaptor;
|
||||
|
||||
@Mock private MessageConverter messageConverter;
|
||||
|
||||
private MethodParameter replyToReturnType;
|
||||
|
||||
private MethodParameter replyToUserReturnType;
|
||||
|
||||
private MethodParameter missingReplyToReturnType;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
Message<String> message = MessageBuilder.withPayload(payloadContent).build();
|
||||
when(this.messageConverter.toMessage(payloadContent)).thenReturn(message);
|
||||
|
||||
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
|
||||
messagingTemplate.setConverter(this.messageConverter);
|
||||
|
||||
this.handler = new ReplyToMethodReturnValueHandler(messagingTemplate);
|
||||
|
||||
Method method = this.getClass().getDeclaredMethod("handleAndReplyTo");
|
||||
this.replyToReturnType = new MethodParameter(method, -1);
|
||||
|
||||
method = this.getClass().getDeclaredMethod("handleAndReplyToUser");
|
||||
this.replyToUserReturnType = new MethodParameter(method, -1);
|
||||
|
||||
method = this.getClass().getDeclaredMethod("handleWithMissingReplyTo");
|
||||
this.missingReplyToReturnType = new MethodParameter(method, -1);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() throws Exception {
|
||||
assertTrue(this.handler.supportsReturnType(this.replyToReturnType));
|
||||
assertTrue(this.handler.supportsReturnType(this.replyToUserReturnType));
|
||||
assertFalse(this.handler.supportsReturnType(this.missingReplyToReturnType));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replyToMethod() throws Exception {
|
||||
|
||||
when(this.messageChannel.send(any(Message.class))).thenReturn(true);
|
||||
|
||||
String sessionId = "sess1";
|
||||
Message<?> inputMessage = createInputMessage(sessionId, "sub1", "/dest", null);
|
||||
|
||||
this.handler.handleReturnValue(payloadContent, this.replyToReturnType, 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());
|
||||
assertNull(headers.getSubscriptionId());
|
||||
assertEquals("/dest1", headers.getDestination());
|
||||
|
||||
message = this.messageCaptor.getAllValues().get(1);
|
||||
headers = SimpMessageHeaderAccessor.wrap(message);
|
||||
|
||||
assertEquals(sessionId, headers.getSessionId());
|
||||
assertNull(headers.getSubscriptionId());
|
||||
assertEquals("/dest2", headers.getDestination());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replyToUserMethod() 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.replyToUserReturnType, 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());
|
||||
assertNull(headers.getSubscriptionId());
|
||||
assertEquals("/user/" + user.getName() + "/dest1", headers.getDestination());
|
||||
|
||||
message = this.messageCaptor.getAllValues().get(1);
|
||||
headers = SimpMessageHeaderAccessor.wrap(message);
|
||||
|
||||
assertEquals(sessionId, headers.getSessionId());
|
||||
assertNull(headers.getSubscriptionId());
|
||||
assertEquals("/user/" + user.getName() + "/dest2", headers.getDestination());
|
||||
}
|
||||
|
||||
|
||||
private Message<?> createInputMessage(String sessId, String subsId, String dest, Principal principal) {
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();
|
||||
headers.setSessionId(sessId);
|
||||
headers.setSubscriptionId(subsId);
|
||||
headers.setDestination(dest);
|
||||
headers.setUser(principal);
|
||||
return MessageBuilder.withPayload(new byte[0]).copyHeaders(headers.toMap()).build();
|
||||
}
|
||||
|
||||
private static class TestUser implements Principal {
|
||||
|
||||
public String getName() {
|
||||
return "joe";
|
||||
}
|
||||
|
||||
public boolean implies(Subject subject) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@MessageMapping("/handle") // not needed for the tests but here for completeness
|
||||
public String handleWithMissingReplyTo() {
|
||||
return payloadContent;
|
||||
}
|
||||
|
||||
@MessageMapping("/handle") // not needed for the tests but here for completeness
|
||||
@ReplyTo({"/dest1", "/dest2"})
|
||||
public String handleAndReplyTo() {
|
||||
return payloadContent;
|
||||
}
|
||||
|
||||
@MessageMapping("/handle") // not needed for the tests but here for completeness
|
||||
@ReplyToUser({"/dest1", "/dest2"})
|
||||
public String handleAndReplyToUser() {
|
||||
return payloadContent;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* 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.lang.reflect.Method;
|
||||
import java.security.Principal;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.handler.annotation.ReplyTo;
|
||||
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
|
||||
import org.springframework.messaging.simp.SimpMessagingTemplate;
|
||||
import org.springframework.messaging.simp.annotation.SubscribeEvent;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.converter.MessageConverter;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
|
||||
/**
|
||||
* Test fixture for {@link SubscriptionMethodReturnValueHandler}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class SubscriptionMethodReturnValueHandlerTests {
|
||||
|
||||
private static final String payloadContent = "payload";
|
||||
|
||||
|
||||
private SubscriptionMethodReturnValueHandler handler;
|
||||
|
||||
@Mock private MessageChannel messageChannel;
|
||||
|
||||
@Captor ArgumentCaptor<Message<?>> messageCaptor;
|
||||
|
||||
@Mock private MessageConverter messageConverter;
|
||||
|
||||
private MethodParameter subscribeEventReturnType;
|
||||
|
||||
private MethodParameter subscribeEventReplyToReturnType;
|
||||
|
||||
private MethodParameter messageMappingReturnType;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
Message<String> message = MessageBuilder.withPayload(payloadContent).build();
|
||||
when(this.messageConverter.toMessage(payloadContent)).thenReturn(message);
|
||||
|
||||
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(this.messageChannel);
|
||||
messagingTemplate.setConverter(this.messageConverter);
|
||||
|
||||
this.handler = new SubscriptionMethodReturnValueHandler(messagingTemplate);
|
||||
|
||||
Method method = this.getClass().getDeclaredMethod("getData");
|
||||
this.subscribeEventReturnType = new MethodParameter(method, -1);
|
||||
|
||||
method = this.getClass().getDeclaredMethod("getDataAndReplyTo");
|
||||
this.subscribeEventReplyToReturnType = new MethodParameter(method, -1);
|
||||
|
||||
method = this.getClass().getDeclaredMethod("handle");
|
||||
this.messageMappingReturnType = new MethodParameter(method, -1);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void supportsReturnType() throws Exception {
|
||||
assertTrue(this.handler.supportsReturnType(this.subscribeEventReturnType));
|
||||
assertFalse(this.handler.supportsReturnType(this.subscribeEventReplyToReturnType));
|
||||
assertFalse(this.handler.supportsReturnType(this.messageMappingReturnType));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subscribeEventMethod() throws Exception {
|
||||
|
||||
when(this.messageChannel.send(any(Message.class))).thenReturn(true);
|
||||
|
||||
String sessionId = "sess1";
|
||||
String subscriptionId = "subs1";
|
||||
String destination = "/dest";
|
||||
Message<?> inputMessage = createInputMessage(sessionId, subscriptionId, destination, null);
|
||||
|
||||
this.handler.handleReturnValue(payloadContent, this.subscribeEventReturnType, inputMessage);
|
||||
|
||||
verify(this.messageChannel).send(this.messageCaptor.capture());
|
||||
assertNotNull(this.messageCaptor.getValue());
|
||||
|
||||
Message<?> message = this.messageCaptor.getValue();
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(message);
|
||||
|
||||
assertEquals("sessionId should always be copied", sessionId, headers.getSessionId());
|
||||
assertEquals(subscriptionId, headers.getSubscriptionId());
|
||||
assertEquals(destination, headers.getDestination());
|
||||
}
|
||||
|
||||
|
||||
private Message<?> createInputMessage(String sessId, String subsId, String dest, Principal principal) {
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create();
|
||||
headers.setSessionId(sessId);
|
||||
headers.setSubscriptionId(subsId);
|
||||
headers.setDestination(dest);
|
||||
headers.setUser(principal);
|
||||
return MessageBuilder.withPayload(new byte[0]).copyHeaders(headers.toMap()).build();
|
||||
}
|
||||
|
||||
|
||||
@SubscribeEvent("/data") // not needed for the tests but here for completeness
|
||||
private String getData() {
|
||||
return payloadContent;
|
||||
}
|
||||
|
||||
@SubscribeEvent("/data") // not needed for the tests but here for completeness
|
||||
@ReplyTo("/replyToDest")
|
||||
private String getDataAndReplyTo() {
|
||||
return payloadContent;
|
||||
}
|
||||
|
||||
@MessageMapping("/handle") // not needed for the tests but here for completeness
|
||||
public String handle() {
|
||||
return payloadContent;
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ import static org.mockito.BDDMockito.*;
|
|||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link TaskExecutorSubscribableChannel}.
|
||||
* Tests for {@link ExecutorSubscribableChannel}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
|
|
@ -46,7 +46,7 @@ public class PublishSubscibeChannelTests {
|
|||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
|
||||
private TaskExecutorSubscribableChannel channel = new TaskExecutorSubscribableChannel();
|
||||
private ExecutorSubscribableChannel channel = new ExecutorSubscribableChannel();
|
||||
|
||||
@Mock
|
||||
private MessageHandler handler;
|
||||
|
|
@ -71,14 +71,6 @@ public class PublishSubscibeChannelTests {
|
|||
this.channel.send(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void payloadMustNotBeNull() throws Exception {
|
||||
Message<?> message = mock(Message.class);
|
||||
thrown.expect(IllegalArgumentException.class);
|
||||
thrown.expectMessage("Message payload must not be null");
|
||||
this.channel.send(message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendWithoutExecutor() {
|
||||
this.channel.subscribe(this.handler);
|
||||
|
|
@ -89,7 +81,7 @@ public class PublishSubscibeChannelTests {
|
|||
@Test
|
||||
public void sendWithExecutor() throws Exception {
|
||||
TaskExecutor executor = mock(TaskExecutor.class);
|
||||
this.channel = new TaskExecutorSubscribableChannel(executor);
|
||||
this.channel = new ExecutorSubscribableChannel(executor);
|
||||
this.channel.subscribe(this.handler);
|
||||
this.channel.send(this.message);
|
||||
verify(executor).execute(this.runnableCaptor.capture());
|
||||
|
|
|
|||
|
|
@ -206,7 +206,7 @@ public class HandlerMethod {
|
|||
* @return the annotation, or {@code null} if none found
|
||||
*/
|
||||
public <A extends Annotation> A getMethodAnnotation(Class<A> annotationType) {
|
||||
return AnnotationUtils.findAnnotation(this.method, annotationType);
|
||||
return AnnotationUtils.findAnnotation(this.bridgedMethod, annotationType);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,11 +32,12 @@ import org.springframework.util.ReflectionUtils.MethodFilter;
|
|||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.method.HandlerMethodSelector;
|
||||
|
||||
|
||||
/**
|
||||
* Discovers {@linkplain ExceptionHandler @ExceptionHandler} methods in a given class
|
||||
* type, including all super types, and helps to resolve an Exception to the method
|
||||
* its mapped to. Exception mappings are defined through {@code @ExceptionHandler}
|
||||
* annotation or by looking at the signature of an {@code @ExceptionHandler} method.
|
||||
* 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 3.1
|
||||
|
|
@ -56,17 +57,13 @@ public class ExceptionHandlerMethodResolver {
|
|||
* @param handlerType the type to introspect
|
||||
*/
|
||||
public ExceptionHandlerMethodResolver(Class<?> handlerType) {
|
||||
for (Method method : HandlerMethodSelector.selectMethods(handlerType, getExceptionHandlerMethods())) {
|
||||
for (Method method : HandlerMethodSelector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {
|
||||
for (Class<? extends Throwable> exceptionType : detectExceptionMappings(method)) {
|
||||
addExceptionMapping(exceptionType, method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected MethodFilter getExceptionHandlerMethods() {
|
||||
return EXCEPTION_HANDLER_METHODS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract exception mappings from the {@code @ExceptionHandler} annotation
|
||||
* first and as a fall-back from the method signature.
|
||||
|
|
@ -91,8 +88,8 @@ public class ExceptionHandlerMethodResolver {
|
|||
}
|
||||
|
||||
protected void detectAnnotationExceptionMappings(Method method, List<Class<? extends Throwable>> result) {
|
||||
ExceptionHandler annotation = AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
|
||||
result.addAll(Arrays.asList(annotation.value()));
|
||||
ExceptionHandler annot = AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
|
||||
result.addAll(Arrays.asList(annot.value()));
|
||||
}
|
||||
|
||||
private void addExceptionMapping(Class<? extends Throwable> exceptionType, Method method) {
|
||||
|
|
@ -146,9 +143,8 @@ public class ExceptionHandlerMethodResolver {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A filter for selecting {@code @ExceptionHandler} methods.
|
||||
*/
|
||||
|
||||
/** A filter for selecting annotated exception handling methods. */
|
||||
public final static MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Reference in New Issue