Polish
Updates to synchronize with newly created reactive equivalents.
This commit is contained in:
@@ -33,24 +33,20 @@ import org.springframework.messaging.handler.invocation.HandlerMethodArgumentRes
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for resolving method arguments from a named value. Message headers,
|
||||
* and path variables are examples of named values. Each may have a name, a required flag,
|
||||
* and a default value.
|
||||
* Abstract base class to resolve method arguments from a named value, e.g.
|
||||
* message headers or destination variables. Named values could have one or more
|
||||
* of a name, a required flag, and a default value.
|
||||
*
|
||||
* <p>Subclasses define how to do the following:
|
||||
* <ul>
|
||||
* <li>Obtain named value information for a method parameter
|
||||
* <li>Resolve names into argument values
|
||||
* <li>Handle missing argument values when argument values are required
|
||||
* <li>Optionally handle a resolved value
|
||||
* </ul>
|
||||
* <p>Subclasses only need to define specific steps such as how to obtain named
|
||||
* value details from a method parameter, how to resolve to argument values, or
|
||||
* how to handle missing values.
|
||||
*
|
||||
* <p>A default value string can contain ${...} placeholders and Spring Expression
|
||||
* Language {@code #{...}} expressions. For this to work a {@link ConfigurableBeanFactory}
|
||||
* must be supplied to the class constructor.
|
||||
* <p>A default value string can contain ${...} placeholders and Spring
|
||||
* Expression Language {@code #{...}} expressions which will be resolved if a
|
||||
* {@link ConfigurableBeanFactory} is supplied to the class constructor.
|
||||
*
|
||||
* <p>A {@link ConversionService} may be used to apply type conversion to the resolved
|
||||
* argument value if it doesn't match the method parameter type.
|
||||
* <p>A {@link ConversionService} is used to to convert resolved String argument
|
||||
* value to the expected target method parameter type.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
@@ -71,11 +67,10 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
|
||||
/**
|
||||
* Constructor with a {@link ConversionService} and a {@link BeanFactory}.
|
||||
* @param conversionService conversion service for converting values to match the
|
||||
* target method parameter type
|
||||
* @param beanFactory a bean factory to use for resolving {@code ${...}} placeholder
|
||||
* and {@code #{...}} SpEL expressions in default values, or {@code null} if default
|
||||
* values are not expected to contain expressions
|
||||
* @param conversionService conversion service for converting String values
|
||||
* to the target method parameter type
|
||||
* @param beanFactory a bean factory for resolving {@code ${...}}
|
||||
* placeholders and {@code #{...}} SpEL expressions in default values
|
||||
*/
|
||||
protected AbstractNamedValueMethodArgumentResolver(ConversionService conversionService,
|
||||
@Nullable ConfigurableBeanFactory beanFactory) {
|
||||
@@ -87,12 +82,12 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Object resolveArgument(MethodParameter parameter, Message<?> message) throws Exception {
|
||||
|
||||
NamedValueInfo namedValueInfo = getNamedValueInfo(parameter);
|
||||
MethodParameter nestedParameter = parameter.nestedIfOptional();
|
||||
|
||||
Object resolvedName = resolveStringValue(namedValueInfo.name);
|
||||
Object resolvedName = resolveEmbeddedValuesAndExpressions(namedValueInfo.name);
|
||||
if (resolvedName == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Specified name must not resolve to null: [" + namedValueInfo.name + "]");
|
||||
@@ -101,7 +96,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
Object arg = resolveArgumentInternal(nestedParameter, message, resolvedName.toString());
|
||||
if (arg == null) {
|
||||
if (namedValueInfo.defaultValue != null) {
|
||||
arg = resolveStringValue(namedValueInfo.defaultValue);
|
||||
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
|
||||
}
|
||||
else if (namedValueInfo.required && !nestedParameter.isOptional()) {
|
||||
handleMissingValue(namedValueInfo.name, nestedParameter, message);
|
||||
@@ -109,7 +104,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
arg = handleNullValue(namedValueInfo.name, arg, nestedParameter.getNestedParameterType());
|
||||
}
|
||||
else if ("".equals(arg) && namedValueInfo.defaultValue != null) {
|
||||
arg = resolveStringValue(namedValueInfo.defaultValue);
|
||||
arg = resolveEmbeddedValuesAndExpressions(namedValueInfo.defaultValue);
|
||||
}
|
||||
|
||||
if (parameter != nestedParameter || !ClassUtils.isAssignableValue(parameter.getParameterType(), arg)) {
|
||||
@@ -135,27 +130,31 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the {@link NamedValueInfo} object for the given method parameter. Implementations typically
|
||||
* retrieve the method annotation by means of {@link MethodParameter#getParameterAnnotation(Class)}.
|
||||
* Create the {@link NamedValueInfo} object for the given method parameter.
|
||||
* Implementations typically retrieve the method annotation by means of
|
||||
* {@link MethodParameter#getParameterAnnotation(Class)}.
|
||||
* @param parameter the method parameter
|
||||
* @return the named value information
|
||||
*/
|
||||
protected abstract NamedValueInfo createNamedValueInfo(MethodParameter parameter);
|
||||
|
||||
/**
|
||||
* Create a new NamedValueInfo based on the given NamedValueInfo with sanitized values.
|
||||
* Fall back on the parameter name from the class file if necessary and
|
||||
* replace {@link ValueConstants#DEFAULT_NONE} with null.
|
||||
*/
|
||||
private NamedValueInfo updateNamedValueInfo(MethodParameter parameter, NamedValueInfo info) {
|
||||
String name = info.name;
|
||||
if (info.name.isEmpty()) {
|
||||
name = parameter.getParameterName();
|
||||
if (name == null) {
|
||||
throw new IllegalArgumentException("Name for argument type [" + parameter.getParameterType().getName() +
|
||||
"] not available, and parameter name information not found in class file either.");
|
||||
Class<?> type = parameter.getParameterType();
|
||||
throw new IllegalArgumentException(
|
||||
"Name for argument of type [" + type.getName() + "] not specified, " +
|
||||
"and parameter name information not found in class file either.");
|
||||
}
|
||||
}
|
||||
String defaultValue = (ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
|
||||
return new NamedValueInfo(name, info.required, defaultValue);
|
||||
return new NamedValueInfo(name, info.required,
|
||||
ValueConstants.DEFAULT_NONE.equals(info.defaultValue) ? null : info.defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,7 +162,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
* potentially containing placeholders and expressions.
|
||||
*/
|
||||
@Nullable
|
||||
private Object resolveStringValue(String value) {
|
||||
private Object resolveEmbeddedValuesAndExpressions(String value) {
|
||||
if (this.configurableBeanFactory == null || this.expressionContext == null) {
|
||||
return value;
|
||||
}
|
||||
@@ -188,18 +187,19 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
throws Exception;
|
||||
|
||||
/**
|
||||
* Invoked when a named value is required, but
|
||||
* {@link #resolveArgumentInternal(MethodParameter, Message, String)} returned {@code null} and
|
||||
* there is no default value. Subclasses typically throw an exception in this case.
|
||||
* Invoked when a value is required, but {@link #resolveArgumentInternal}
|
||||
* returned {@code null} and there is no default value. Sub-classes can
|
||||
* throw an appropriate exception for this case.
|
||||
* @param name the name for the value
|
||||
* @param parameter the method parameter
|
||||
* @param parameter the target method parameter
|
||||
* @param message the message being processed
|
||||
*/
|
||||
protected abstract void handleMissingValue(String name, MethodParameter parameter, Message<?> message);
|
||||
|
||||
/**
|
||||
* A {@code null} results in a {@code false} value for {@code boolean}s or an
|
||||
* exception for other primitives.
|
||||
* One last chance to handle a possible null value.
|
||||
* Specifically for booleans method parameters, use {@link Boolean#FALSE}.
|
||||
* Also raise an ISE for primitive types.
|
||||
*/
|
||||
@Nullable
|
||||
private Object handleNullValue(String name, @Nullable Object value, Class<?> paramType) {
|
||||
@@ -230,8 +230,7 @@ public abstract class AbstractNamedValueMethodArgumentResolver implements Handle
|
||||
|
||||
|
||||
/**
|
||||
* Represents the information about a named value, including name, whether it's
|
||||
* required and a default value.
|
||||
* Represents a named value declaration.
|
||||
*/
|
||||
protected static class NamedValueInfo {
|
||||
|
||||
|
||||
@@ -23,22 +23,20 @@ import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.handler.annotation.DestinationVariable;
|
||||
import org.springframework.messaging.handler.annotation.ValueConstants;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Resolves method parameters annotated with
|
||||
* {@link org.springframework.messaging.handler.annotation.DestinationVariable @DestinationVariable}.
|
||||
* Resolve for {@link DestinationVariable @DestinationVariable} method parameters.
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @since 4.0
|
||||
*/
|
||||
public class DestinationVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
/**
|
||||
* The name of the header used to for template variables.
|
||||
*/
|
||||
/** The name of the header used to for template variables. */
|
||||
public static final String DESTINATION_TEMPLATE_VARIABLES_HEADER =
|
||||
DestinationVariableMethodArgumentResolver.class.getSimpleName() + ".templateVariables";
|
||||
|
||||
@@ -55,26 +53,24 @@ public class DestinationVariableMethodArgumentResolver extends AbstractNamedValu
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
DestinationVariable annotation = parameter.getParameterAnnotation(DestinationVariable.class);
|
||||
Assert.state(annotation != null, "No DestinationVariable annotation");
|
||||
return new DestinationVariableNamedValueInfo(annotation);
|
||||
DestinationVariable annot = parameter.getParameterAnnotation(DestinationVariable.class);
|
||||
Assert.state(annot != null, "No DestinationVariable annotation");
|
||||
return new DestinationVariableNamedValueInfo(annot);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
protected Object resolveArgumentInternal(MethodParameter parameter, Message<?> message, String name)
|
||||
throws Exception {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> vars =
|
||||
(Map<String, String>) message.getHeaders().get(DESTINATION_TEMPLATE_VARIABLES_HEADER);
|
||||
return (vars != null ? vars.get(name) : null);
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object resolveArgumentInternal(MethodParameter parameter, Message<?> message, String name) {
|
||||
MessageHeaders headers = message.getHeaders();
|
||||
Map<String, String> vars = (Map<String, String>) headers.get(DESTINATION_TEMPLATE_VARIABLES_HEADER);
|
||||
return vars != null ? vars.get(name) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleMissingValue(String name, MethodParameter parameter, Message<?> message) {
|
||||
throw new MessageHandlingException(message, "Missing path template variable '" + name +
|
||||
"' for method parameter type [" + parameter.getParameterType() + "]");
|
||||
throw new MessageHandlingException(message, "Missing path template variable '" + name + "' " +
|
||||
"for method parameter type [" + parameter.getParameterType() + "]");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -33,10 +33,15 @@ import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Resolves method parameters annotated with {@link Header @Header}.
|
||||
* Resolver for {@link Header @Header} arguments. Headers are resolved from
|
||||
* either the top-level header map or the nested
|
||||
* {@link NativeMessageHeaderAccessor native} header map.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.0
|
||||
*
|
||||
* @see HeadersMethodArgumentResolver
|
||||
* @see NativeMessageHeaderAccessor
|
||||
*/
|
||||
public class HeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
|
||||
|
||||
@@ -57,9 +62,9 @@ public class HeaderMethodArgumentResolver extends AbstractNamedValueMethodArgume
|
||||
|
||||
@Override
|
||||
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
|
||||
Header annotation = parameter.getParameterAnnotation(Header.class);
|
||||
Assert.state(annotation != null, "No Header annotation");
|
||||
return new HeaderNamedValueInfo(annotation);
|
||||
Header annot = parameter.getParameterAnnotation(Header.class);
|
||||
Assert.state(annot != null, "No Header annotation");
|
||||
return new HeaderNamedValueInfo(annot);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,10 +77,9 @@ public class HeaderMethodArgumentResolver extends AbstractNamedValueMethodArgume
|
||||
|
||||
if (headerValue != null && nativeHeaderValue != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Message headers contain two values for the same header '" + name + "', " +
|
||||
"one in the top level header map and a second in the nested map with native headers. " +
|
||||
"Using the value from top level map. " +
|
||||
"Use 'nativeHeader.myHeader' to resolve to the value from the nested native header map.");
|
||||
logger.debug("A value was found for '" + name + "', in both the top level header map " +
|
||||
"and also in the nested map for native headers. Using the value from top level map. " +
|
||||
"Use 'nativeHeader.myHeader' to resolve the native header.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -29,12 +29,11 @@ import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link HandlerMethodArgumentResolver} for header method parameters. Resolves the
|
||||
* following method parameters:
|
||||
* Argument resolver for headers. Resolves the following method parameters:
|
||||
* <ul>
|
||||
* <li>Parameters assignable to {@link Map} annotated with {@link Headers @Headers}
|
||||
* <li>Parameters of type {@link MessageHeaders}
|
||||
* <li>Parameters assignable to {@link MessageHeaderAccessor}
|
||||
* <li>{@link Headers @Headers} {@link Map}
|
||||
* <li>{@link MessageHeaders}
|
||||
* <li>{@link MessageHeaderAccessor}
|
||||
* </ul>
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
@@ -58,7 +57,7 @@ public class HeadersMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||
}
|
||||
else if (MessageHeaderAccessor.class == paramType) {
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, MessageHeaderAccessor.class);
|
||||
return (accessor != null ? accessor : new MessageHeaderAccessor(message));
|
||||
return accessor != null ? accessor : new MessageHeaderAccessor(message);
|
||||
}
|
||||
else if (MessageHeaderAccessor.class.isAssignableFrom(paramType)) {
|
||||
MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, MessageHeaderAccessor.class);
|
||||
@@ -75,9 +74,8 @@ public class HeadersMethodArgumentResolver implements HandlerMethodArgumentResol
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"Unexpected method parameter type " + paramType + "in method " + parameter.getMethod() + ". "
|
||||
+ "@Headers method arguments must be assignable to java.util.Map.");
|
||||
throw new IllegalStateException("Unexpected parameter of type " + paramType +
|
||||
" in method " + parameter.getMethod() + ". ");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -16,23 +16,21 @@
|
||||
|
||||
package org.springframework.messaging.handler.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.messaging.handler.annotation.DestinationVariable;
|
||||
import org.springframework.messaging.handler.invocation.ResolvableMethod;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.messaging.handler.annotation.MessagingPredicates.*;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link DestinationVariableMethodArgumentResolver} tests.
|
||||
@@ -41,33 +39,17 @@ import static org.junit.Assert.*;
|
||||
*/
|
||||
public class DestinationVariableMethodArgumentResolverTests {
|
||||
|
||||
private DestinationVariableMethodArgumentResolver resolver;
|
||||
private final DestinationVariableMethodArgumentResolver resolver =
|
||||
new DestinationVariableMethodArgumentResolver(new DefaultConversionService());
|
||||
|
||||
private MethodParameter paramAnnotated;
|
||||
private MethodParameter paramAnnotatedValue;
|
||||
private MethodParameter paramNotAnnotated;
|
||||
private final ResolvableMethod resolvable =
|
||||
ResolvableMethod.on(getClass()).named("handleMessage").build();
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
this.resolver = new DestinationVariableMethodArgumentResolver(new DefaultConversionService());
|
||||
|
||||
Method method = getClass().getDeclaredMethod("handleMessage", String.class, String.class, String.class);
|
||||
this.paramAnnotated = new MethodParameter(method, 0);
|
||||
this.paramAnnotatedValue = new MethodParameter(method, 1);
|
||||
this.paramNotAnnotated = new MethodParameter(method, 2);
|
||||
|
||||
this.paramAnnotated.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
GenericTypeResolver.resolveParameterType(this.paramAnnotated, DestinationVariableMethodArgumentResolver.class);
|
||||
this.paramAnnotatedValue.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
GenericTypeResolver.resolveParameterType(this.paramAnnotatedValue, DestinationVariableMethodArgumentResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue(resolver.supportsParameter(paramAnnotated));
|
||||
assertTrue(resolver.supportsParameter(paramAnnotatedValue));
|
||||
assertFalse(resolver.supportsParameter(paramNotAnnotated));
|
||||
assertTrue(resolver.supportsParameter(this.resolvable.annot(destinationVar().noValue()).arg()));
|
||||
assertFalse(resolver.supportsParameter(this.resolvable.annotNotPresent(DestinationVariable.class).arg()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,17 +62,19 @@ public class DestinationVariableMethodArgumentResolverTests {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeader(
|
||||
DestinationVariableMethodArgumentResolver.DESTINATION_TEMPLATE_VARIABLES_HEADER, vars).build();
|
||||
|
||||
Object result = this.resolver.resolveArgument(this.paramAnnotated, message);
|
||||
MethodParameter param = this.resolvable.annot(destinationVar().noValue()).arg();
|
||||
Object result = this.resolver.resolveArgument(param, message);
|
||||
assertEquals("bar", result);
|
||||
|
||||
result = this.resolver.resolveArgument(this.paramAnnotatedValue, message);
|
||||
param = this.resolvable.annot(destinationVar("name")).arg();
|
||||
result = this.resolver.resolveArgument(param, message);
|
||||
assertEquals("value", result);
|
||||
}
|
||||
|
||||
@Test(expected = MessageHandlingException.class)
|
||||
public void resolveArgumentNotFound() throws Exception {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).build();
|
||||
this.resolver.resolveArgument(this.paramAnnotated, message);
|
||||
this.resolver.resolveArgument(this.resolvable.annot(destinationVar().noValue()).arg(), message);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.messaging.handler.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -25,19 +24,17 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.annotation.SynthesizingMethodParameter;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHandlingException;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import org.springframework.messaging.handler.invocation.ResolvableMethod;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.messaging.handler.annotation.MessagingPredicates.*;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link HeaderMethodArgumentResolver} tests.
|
||||
@@ -50,46 +47,27 @@ public class HeaderMethodArgumentResolverTests {
|
||||
|
||||
private HeaderMethodArgumentResolver resolver;
|
||||
|
||||
private MethodParameter paramRequired;
|
||||
private MethodParameter paramNamedDefaultValueStringHeader;
|
||||
private MethodParameter paramSystemPropertyDefaultValue;
|
||||
private MethodParameter paramSystemPropertyName;
|
||||
private MethodParameter paramNotAnnotated;
|
||||
private MethodParameter paramOptional;
|
||||
private MethodParameter paramNativeHeader;
|
||||
private final ResolvableMethod resolvable = ResolvableMethod.on(getClass()).named("handleMessage").build();
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@SuppressWarnings("resource")
|
||||
GenericApplicationContext cxt = new GenericApplicationContext();
|
||||
cxt.refresh();
|
||||
this.resolver = new HeaderMethodArgumentResolver(new DefaultConversionService(), cxt.getBeanFactory());
|
||||
|
||||
Method method = ReflectionUtils.findMethod(getClass(), "handleMessage", (Class<?>[]) null);
|
||||
this.paramRequired = new SynthesizingMethodParameter(method, 0);
|
||||
this.paramNamedDefaultValueStringHeader = new SynthesizingMethodParameter(method, 1);
|
||||
this.paramSystemPropertyDefaultValue = new SynthesizingMethodParameter(method, 2);
|
||||
this.paramSystemPropertyName = new SynthesizingMethodParameter(method, 3);
|
||||
this.paramNotAnnotated = new SynthesizingMethodParameter(method, 4);
|
||||
this.paramOptional = new SynthesizingMethodParameter(method, 5);
|
||||
this.paramNativeHeader = new SynthesizingMethodParameter(method, 6);
|
||||
|
||||
this.paramRequired.initParameterNameDiscovery(new DefaultParameterNameDiscoverer());
|
||||
GenericTypeResolver.resolveParameterType(this.paramRequired, HeaderMethodArgumentResolver.class);
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.refresh();
|
||||
this.resolver = new HeaderMethodArgumentResolver(new DefaultConversionService(), context.getBeanFactory());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue(resolver.supportsParameter(paramNamedDefaultValueStringHeader));
|
||||
assertFalse(resolver.supportsParameter(paramNotAnnotated));
|
||||
assertTrue(this.resolver.supportsParameter(this.resolvable.annot(headerPlain()).arg()));
|
||||
assertFalse(this.resolver.supportsParameter(this.resolvable.annotNotPresent(Header.class).arg()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() throws Exception {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeader("param1", "foo").build();
|
||||
Object result = this.resolver.resolveArgument(this.paramRequired, message);
|
||||
Object result = this.resolver.resolveArgument(this.resolvable.annot(headerPlain()).arg(), message);
|
||||
assertEquals("foo", result);
|
||||
}
|
||||
|
||||
@@ -98,7 +76,7 @@ public class HeaderMethodArgumentResolverTests {
|
||||
TestMessageHeaderAccessor headers = new TestMessageHeaderAccessor();
|
||||
headers.setNativeHeader("param1", "foo");
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
|
||||
assertEquals("foo", this.resolver.resolveArgument(this.paramRequired, message));
|
||||
assertEquals("foo", this.resolver.resolveArgument(this.resolvable.annot(headerPlain()).arg(), message));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,20 +86,23 @@ public class HeaderMethodArgumentResolverTests {
|
||||
headers.setNativeHeader("param1", "native-foo");
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
|
||||
|
||||
assertEquals("foo", this.resolver.resolveArgument(this.paramRequired, message));
|
||||
assertEquals("native-foo", this.resolver.resolveArgument(this.paramNativeHeader, message));
|
||||
assertEquals("foo", this.resolver.resolveArgument(
|
||||
this.resolvable.annot(headerPlain()).arg(), message));
|
||||
|
||||
assertEquals("native-foo", this.resolver.resolveArgument(
|
||||
this.resolvable.annot(header("nativeHeaders.param1")).arg(), message));
|
||||
}
|
||||
|
||||
@Test(expected = MessageHandlingException.class)
|
||||
public void resolveArgumentNotFound() throws Exception {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).build();
|
||||
this.resolver.resolveArgument(this.paramRequired, message);
|
||||
this.resolver.resolveArgument(this.resolvable.annot(headerPlain()).arg(), message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentDefaultValue() throws Exception {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).build();
|
||||
Object result = this.resolver.resolveArgument(this.paramNamedDefaultValueStringHeader, message);
|
||||
Object result = this.resolver.resolveArgument(this.resolvable.annot(header("name", "bar")).arg(), message);
|
||||
assertEquals("bar", result);
|
||||
}
|
||||
|
||||
@@ -130,7 +111,8 @@ public class HeaderMethodArgumentResolverTests {
|
||||
System.setProperty("systemProperty", "sysbar");
|
||||
try {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).build();
|
||||
Object result = resolver.resolveArgument(paramSystemPropertyDefaultValue, message);
|
||||
MethodParameter param = this.resolvable.annot(header("name", "#{systemProperties.systemProperty}")).arg();
|
||||
Object result = resolver.resolveArgument(param, message);
|
||||
assertEquals("sysbar", result);
|
||||
}
|
||||
finally {
|
||||
@@ -143,7 +125,8 @@ public class HeaderMethodArgumentResolverTests {
|
||||
System.setProperty("systemProperty", "sysbar");
|
||||
try {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeader("sysbar", "foo").build();
|
||||
Object result = resolver.resolveArgument(paramSystemPropertyName, message);
|
||||
MethodParameter param = this.resolvable.annot(header("#{systemProperties.systemProperty}")).arg();
|
||||
Object result = resolver.resolveArgument(param, message);
|
||||
assertEquals("foo", result);
|
||||
}
|
||||
finally {
|
||||
@@ -153,31 +136,22 @@ public class HeaderMethodArgumentResolverTests {
|
||||
|
||||
@Test
|
||||
public void resolveOptionalHeaderWithValue() throws Exception {
|
||||
GenericApplicationContext cxt = new GenericApplicationContext();
|
||||
cxt.refresh();
|
||||
|
||||
HeaderMethodArgumentResolver resolver =
|
||||
new HeaderMethodArgumentResolver(new DefaultConversionService(), cxt.getBeanFactory());
|
||||
|
||||
Message<String> message = MessageBuilder.withPayload("foo").setHeader("foo", "bar").build();
|
||||
Object result = resolver.resolveArgument(paramOptional, message);
|
||||
MethodParameter param = this.resolvable.annot(header("foo")).arg(Optional.class, String.class);
|
||||
Object result = resolver.resolveArgument(param, message);
|
||||
assertEquals(Optional.of("bar"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveOptionalHeaderAsEmpty() throws Exception {
|
||||
GenericApplicationContext cxt = new GenericApplicationContext();
|
||||
cxt.refresh();
|
||||
|
||||
HeaderMethodArgumentResolver resolver =
|
||||
new HeaderMethodArgumentResolver(new DefaultConversionService(), cxt.getBeanFactory());
|
||||
|
||||
Message<String> message = MessageBuilder.withPayload("foo").build();
|
||||
Object result = resolver.resolveArgument(paramOptional, message);
|
||||
MethodParameter param = this.resolvable.annot(header("foo")).arg(Optional.class, String.class);
|
||||
Object result = resolver.resolveArgument(param, message);
|
||||
assertEquals(Optional.empty(), result);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"})
|
||||
public void handleMessage(
|
||||
@Header String param1,
|
||||
@Header(name = "name", defaultValue = "bar") String param2,
|
||||
@@ -191,7 +165,7 @@ public class HeaderMethodArgumentResolverTests {
|
||||
|
||||
public static class TestMessageHeaderAccessor extends NativeMessageHeaderAccessor {
|
||||
|
||||
protected TestMessageHeaderAccessor() {
|
||||
TestMessageHeaderAccessor() {
|
||||
super((Map<String, List<String>>) null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -16,17 +16,16 @@
|
||||
|
||||
package org.springframework.messaging.handler.annotation.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.handler.annotation.Headers;
|
||||
import org.springframework.messaging.handler.invocation.ResolvableMethod;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.MessageHeaderAccessor;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
@@ -41,47 +40,31 @@ import static org.junit.Assert.*;
|
||||
*/
|
||||
public class HeadersMethodArgumentResolverTests {
|
||||
|
||||
private HeadersMethodArgumentResolver resolver;
|
||||
private final HeadersMethodArgumentResolver resolver = new HeadersMethodArgumentResolver();
|
||||
|
||||
private MethodParameter paramAnnotated;
|
||||
private MethodParameter paramAnnotatedNotMap;
|
||||
private MethodParameter paramMessageHeaders;
|
||||
private MethodParameter paramMessageHeaderAccessor;
|
||||
private MethodParameter paramMessageHeaderAccessorSubclass;
|
||||
private Message<byte[]> message =
|
||||
MessageBuilder.withPayload(new byte[0]).copyHeaders(Collections.singletonMap("foo", "bar")).build();
|
||||
|
||||
private Message<byte[]> message;
|
||||
private final ResolvableMethod resolvable = ResolvableMethod.on(getClass()).named("handleMessage").build();
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
this.resolver = new HeadersMethodArgumentResolver();
|
||||
|
||||
Method method = getClass().getDeclaredMethod("handleMessage", Map.class, String.class,
|
||||
MessageHeaders.class, MessageHeaderAccessor.class, TestMessageHeaderAccessor.class);
|
||||
|
||||
this.paramAnnotated = new MethodParameter(method, 0);
|
||||
this.paramAnnotatedNotMap = new MethodParameter(method, 1);
|
||||
this.paramMessageHeaders = new MethodParameter(method, 2);
|
||||
this.paramMessageHeaderAccessor = new MethodParameter(method, 3);
|
||||
this.paramMessageHeaderAccessorSubclass = new MethodParameter(method, 4);
|
||||
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put("foo", "bar");
|
||||
this.message = MessageBuilder.withPayload(new byte[0]).copyHeaders(headers).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue(this.resolver.supportsParameter(this.paramAnnotated));
|
||||
assertFalse(this.resolver.supportsParameter(this.paramAnnotatedNotMap));
|
||||
assertTrue(this.resolver.supportsParameter(this.paramMessageHeaders));
|
||||
assertTrue(this.resolver.supportsParameter(this.paramMessageHeaderAccessor));
|
||||
assertTrue(this.resolver.supportsParameter(this.paramMessageHeaderAccessorSubclass));
|
||||
|
||||
assertTrue(this.resolver.supportsParameter(
|
||||
this.resolvable.annotPresent(Headers.class).arg(Map.class, String.class, Object.class)));
|
||||
|
||||
assertTrue(this.resolver.supportsParameter(this.resolvable.arg(MessageHeaders.class)));
|
||||
assertTrue(this.resolver.supportsParameter(this.resolvable.arg(MessageHeaderAccessor.class)));
|
||||
assertTrue(this.resolver.supportsParameter(this.resolvable.arg(TestMessageHeaderAccessor.class)));
|
||||
|
||||
assertFalse(this.resolver.supportsParameter(this.resolvable.annotPresent(Headers.class).arg(String.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentAnnotated() throws Exception {
|
||||
Object resolved = this.resolver.resolveArgument(this.paramAnnotated, this.message);
|
||||
MethodParameter param = this.resolvable.annotPresent(Headers.class).arg(Map.class, String.class, Object.class);
|
||||
Object resolved = this.resolver.resolveArgument(param, this.message);
|
||||
|
||||
assertTrue(resolved instanceof Map);
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -91,12 +74,12 @@ public class HeadersMethodArgumentResolverTests {
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void resolveArgumentAnnotatedNotMap() throws Exception {
|
||||
this.resolver.resolveArgument(this.paramAnnotatedNotMap, this.message);
|
||||
this.resolver.resolveArgument(this.resolvable.annotPresent(Headers.class).arg(String.class), this.message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentMessageHeaders() throws Exception {
|
||||
Object resolved = this.resolver.resolveArgument(this.paramMessageHeaders, this.message);
|
||||
Object resolved = this.resolver.resolveArgument(this.resolvable.arg(MessageHeaders.class), this.message);
|
||||
|
||||
assertTrue(resolved instanceof MessageHeaders);
|
||||
MessageHeaders headers = (MessageHeaders) resolved;
|
||||
@@ -105,7 +88,8 @@ public class HeadersMethodArgumentResolverTests {
|
||||
|
||||
@Test
|
||||
public void resolveArgumentMessageHeaderAccessor() throws Exception {
|
||||
Object resolved = this.resolver.resolveArgument(this.paramMessageHeaderAccessor, this.message);
|
||||
MethodParameter param = this.resolvable.arg(MessageHeaderAccessor.class);
|
||||
Object resolved = this.resolver.resolveArgument(param, this.message);
|
||||
|
||||
assertTrue(resolved instanceof MessageHeaderAccessor);
|
||||
MessageHeaderAccessor headers = (MessageHeaderAccessor) resolved;
|
||||
@@ -114,7 +98,8 @@ public class HeadersMethodArgumentResolverTests {
|
||||
|
||||
@Test
|
||||
public void resolveArgumentMessageHeaderAccessorSubclass() throws Exception {
|
||||
Object resolved = this.resolver.resolveArgument(this.paramMessageHeaderAccessorSubclass, this.message);
|
||||
MethodParameter param = this.resolvable.arg(TestMessageHeaderAccessor.class);
|
||||
Object resolved = this.resolver.resolveArgument(param, this.message);
|
||||
|
||||
assertTrue(resolved instanceof TestMessageHeaderAccessor);
|
||||
TestMessageHeaderAccessor headers = (TestMessageHeaderAccessor) resolved;
|
||||
@@ -124,7 +109,7 @@ public class HeadersMethodArgumentResolverTests {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handleMessage(
|
||||
@Headers Map<String, ?> param1,
|
||||
@Headers Map<String, Object> param1,
|
||||
@Headers String param2,
|
||||
MessageHeaders param3,
|
||||
MessageHeaderAccessor param4,
|
||||
@@ -134,7 +119,7 @@ public class HeadersMethodArgumentResolverTests {
|
||||
|
||||
public static class TestMessageHeaderAccessor extends NativeMessageHeaderAccessor {
|
||||
|
||||
protected TestMessageHeaderAccessor(Message<?> message) {
|
||||
TestMessageHeaderAccessor(Message<?> message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user