Resolvers for destination vars and headers
See gh-21987
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Predicates for messaging annotations.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class MessagingPredicates {
|
||||
|
||||
public static DestinationVariablePredicate destinationVar() {
|
||||
return new DestinationVariablePredicate();
|
||||
}
|
||||
|
||||
public static DestinationVariablePredicate destinationVar(String value) {
|
||||
return new DestinationVariablePredicate().value(value);
|
||||
}
|
||||
|
||||
public static HeaderPredicate header() {
|
||||
return new HeaderPredicate();
|
||||
}
|
||||
|
||||
public static HeaderPredicate header(String name) {
|
||||
return new HeaderPredicate().name(name);
|
||||
}
|
||||
|
||||
public static HeaderPredicate header(String name, String defaultValue) {
|
||||
return new HeaderPredicate().name(name).defaultValue(defaultValue);
|
||||
}
|
||||
|
||||
public static HeaderPredicate headerPlain() {
|
||||
return new HeaderPredicate().noAttributes();
|
||||
}
|
||||
|
||||
|
||||
public static class DestinationVariablePredicate implements Predicate<MethodParameter> {
|
||||
|
||||
@Nullable
|
||||
private String value;
|
||||
|
||||
|
||||
public DestinationVariablePredicate value(@Nullable String name) {
|
||||
this.value = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DestinationVariablePredicate noValue() {
|
||||
this.value = "";
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(MethodParameter parameter) {
|
||||
DestinationVariable annotation = parameter.getParameterAnnotation(DestinationVariable.class);
|
||||
return annotation != null && (this.value == null || annotation.value().equals(this.value));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class HeaderPredicate implements Predicate<MethodParameter> {
|
||||
|
||||
@Nullable
|
||||
private String name;
|
||||
|
||||
@Nullable
|
||||
private Boolean required;
|
||||
|
||||
@Nullable
|
||||
private String defaultValue;
|
||||
|
||||
|
||||
public HeaderPredicate name(@Nullable String name) {
|
||||
this.name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HeaderPredicate noName() {
|
||||
this.name = "";
|
||||
return this;
|
||||
}
|
||||
|
||||
public HeaderPredicate required(boolean required) {
|
||||
this.required = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HeaderPredicate defaultValue(@Nullable String value) {
|
||||
this.defaultValue = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HeaderPredicate noAttributes() {
|
||||
this.name = "";
|
||||
this.required = true;
|
||||
this.defaultValue = ValueConstants.DEFAULT_NONE;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(MethodParameter parameter) {
|
||||
Header annotation = parameter.getParameterAnnotation(Header.class);
|
||||
return annotation != null &&
|
||||
(this.name == null || annotation.name().equals(this.name)) &&
|
||||
(this.required == null || annotation.required() == this.required) &&
|
||||
(this.defaultValue == null || annotation.defaultValue().equals(this.defaultValue));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.reactive;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
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.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class DestinationVariableMethodArgumentResolverTests {
|
||||
|
||||
private final DestinationVariableMethodArgumentResolver resolver =
|
||||
new DestinationVariableMethodArgumentResolver(new DefaultConversionService());
|
||||
|
||||
private final ResolvableMethod resolvable =
|
||||
ResolvableMethod.on(getClass()).named("handleMessage").build();
|
||||
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue(resolver.supportsParameter(this.resolvable.annot(destinationVar().noValue()).arg()));
|
||||
assertFalse(resolver.supportsParameter(this.resolvable.annotNotPresent(DestinationVariable.class).arg()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() {
|
||||
|
||||
Map<String, Object> vars = new HashMap<>();
|
||||
vars.put("foo", "bar");
|
||||
vars.put("name", "value");
|
||||
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeader(
|
||||
DestinationVariableMethodArgumentResolver.DESTINATION_TEMPLATE_VARIABLES_HEADER, vars).build();
|
||||
|
||||
Object result = resolveArgument(this.resolvable.annot(destinationVar().noValue()).arg(), message);
|
||||
assertEquals("bar", result);
|
||||
|
||||
result = resolveArgument(this.resolvable.annot(destinationVar("name")).arg(), message);
|
||||
assertEquals("value", result);
|
||||
}
|
||||
|
||||
@Test(expected = MessageHandlingException.class)
|
||||
public void resolveArgumentNotFound() {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).build();
|
||||
resolveArgument(this.resolvable.annot(destinationVar().noValue()).arg(), message);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||
private <T> T resolveArgument(MethodParameter param, Message<?> message) {
|
||||
return (T) this.resolver.resolveArgument(param, message).block(Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handleMessage(
|
||||
@DestinationVariable String foo,
|
||||
@DestinationVariable(value = "name") String param1,
|
||||
String param3) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.reactive;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
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.Header;
|
||||
import org.springframework.messaging.handler.invocation.ResolvableMethod;
|
||||
import org.springframework.messaging.support.MessageBuilder;
|
||||
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.messaging.handler.annotation.MessagingPredicates.*;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link HeaderMethodArgumentResolver} tests.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HeaderMethodArgumentResolverTests {
|
||||
|
||||
private HeaderMethodArgumentResolver resolver;
|
||||
|
||||
private final ResolvableMethod resolvable = ResolvableMethod.on(getClass()).named("handleMessage").build();
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
context.refresh();
|
||||
this.resolver = new HeaderMethodArgumentResolver(new DefaultConversionService(), context.getBeanFactory());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
assertTrue(this.resolver.supportsParameter(this.resolvable.annot(headerPlain()).arg()));
|
||||
assertFalse(this.resolver.supportsParameter(this.resolvable.annotNotPresent(Header.class).arg()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgument() {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeader("param1", "foo").build();
|
||||
Object result = resolveArgument(this.resolvable.annot(headerPlain()).arg(), message);
|
||||
assertEquals("foo", result);
|
||||
}
|
||||
|
||||
@Test // SPR-11326
|
||||
public void resolveArgumentNativeHeader() {
|
||||
TestMessageHeaderAccessor headers = new TestMessageHeaderAccessor();
|
||||
headers.setNativeHeader("param1", "foo");
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
|
||||
assertEquals("foo", resolveArgument(this.resolvable.annot(headerPlain()).arg(), message));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentNativeHeaderAmbiguity() {
|
||||
TestMessageHeaderAccessor headers = new TestMessageHeaderAccessor();
|
||||
headers.setHeader("param1", "foo");
|
||||
headers.setNativeHeader("param1", "native-foo");
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
|
||||
|
||||
assertEquals("foo", resolveArgument(
|
||||
this.resolvable.annot(headerPlain()).arg(), message));
|
||||
|
||||
assertEquals("native-foo", resolveArgument(
|
||||
this.resolvable.annot(header("nativeHeaders.param1")).arg(), message));
|
||||
}
|
||||
|
||||
@Test(expected = MessageHandlingException.class)
|
||||
public void resolveArgumentNotFound() {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).build();
|
||||
resolveArgument(this.resolvable.annot(headerPlain()).arg(), message);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentDefaultValue() {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).build();
|
||||
Object result = resolveArgument(this.resolvable.annot(header("name", "bar")).arg(), message);
|
||||
assertEquals("bar", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveDefaultValueSystemProperty() {
|
||||
System.setProperty("systemProperty", "sysbar");
|
||||
try {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).build();
|
||||
MethodParameter param = this.resolvable.annot(header("name", "#{systemProperties.systemProperty}")).arg();
|
||||
Object result = resolveArgument(param, message);
|
||||
assertEquals("sysbar", result);
|
||||
}
|
||||
finally {
|
||||
System.clearProperty("systemProperty");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveNameFromSystemProperty() {
|
||||
System.setProperty("systemProperty", "sysbar");
|
||||
try {
|
||||
Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeader("sysbar", "foo").build();
|
||||
MethodParameter param = this.resolvable.annot(header("#{systemProperties.systemProperty}")).arg();
|
||||
Object result = resolveArgument(param, message);
|
||||
assertEquals("foo", result);
|
||||
}
|
||||
finally {
|
||||
System.clearProperty("systemProperty");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveOptionalHeaderWithValue() {
|
||||
Message<String> message = MessageBuilder.withPayload("foo").setHeader("foo", "bar").build();
|
||||
MethodParameter param = this.resolvable.annot(header("foo")).arg(Optional.class, String.class);
|
||||
Object result = resolveArgument(param, message);
|
||||
assertEquals(Optional.of("bar"), result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveOptionalHeaderAsEmpty() {
|
||||
Message<String> message = MessageBuilder.withPayload("foo").build();
|
||||
MethodParameter param = this.resolvable.annot(header("foo")).arg(Optional.class, String.class);
|
||||
Object result = resolveArgument(param, message);
|
||||
assertEquals(Optional.empty(), result);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||
private <T> T resolveArgument(MethodParameter param, Message<?> message) {
|
||||
return (T) this.resolver.resolveArgument(param, message).block(Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType"})
|
||||
public void handleMessage(
|
||||
@Header String param1,
|
||||
@Header(name = "name", defaultValue = "bar") String param2,
|
||||
@Header(name = "name", defaultValue = "#{systemProperties.systemProperty}") String param3,
|
||||
@Header(name = "#{systemProperties.systemProperty}") String param4,
|
||||
String param5,
|
||||
@Header("foo") Optional<String> param6,
|
||||
@Header("nativeHeaders.param1") String nativeHeaderParam1) {
|
||||
}
|
||||
|
||||
|
||||
public static class TestMessageHeaderAccessor extends NativeMessageHeaderAccessor {
|
||||
|
||||
TestMessageHeaderAccessor() {
|
||||
super((Map<String, List<String>>) null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.reactive;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
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;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Test fixture for {@link HeadersMethodArgumentResolver} tests.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HeadersMethodArgumentResolverTests {
|
||||
|
||||
private final HeadersMethodArgumentResolver resolver = new HeadersMethodArgumentResolver();
|
||||
|
||||
private Message<byte[]> message =
|
||||
MessageBuilder.withPayload(new byte[0]).copyHeaders(Collections.singletonMap("foo", "bar")).build();
|
||||
|
||||
private final ResolvableMethod resolvable = ResolvableMethod.on(getClass()).named("handleMessage").build();
|
||||
|
||||
|
||||
@Test
|
||||
public void supportsParameter() {
|
||||
|
||||
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
|
||||
@SuppressWarnings("unchecked")
|
||||
public void resolveArgumentAnnotated() {
|
||||
MethodParameter param = this.resolvable.annotPresent(Headers.class).arg(Map.class, String.class, Object.class);
|
||||
Map<String, Object> headers = resolveArgument(param);
|
||||
assertEquals("bar", headers.get("foo"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void resolveArgumentAnnotatedNotMap() {
|
||||
resolveArgument(this.resolvable.annotPresent(Headers.class).arg(String.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentMessageHeaders() {
|
||||
MessageHeaders headers = resolveArgument(this.resolvable.arg(MessageHeaders.class));
|
||||
assertEquals("bar", headers.get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentMessageHeaderAccessor() {
|
||||
MessageHeaderAccessor headers = resolveArgument(this.resolvable.arg(MessageHeaderAccessor.class));
|
||||
assertEquals("bar", headers.getHeader("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resolveArgumentMessageHeaderAccessorSubclass() {
|
||||
TestMessageHeaderAccessor headers = resolveArgument(this.resolvable.arg(TestMessageHeaderAccessor.class));
|
||||
assertEquals("bar", headers.getHeader("foo"));
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "ConstantConditions"})
|
||||
private <T> T resolveArgument(MethodParameter param) {
|
||||
return (T) this.resolver.resolveArgument(param, this.message).block(Duration.ofSeconds(5));
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private void handleMessage(
|
||||
@Headers Map<String, Object> param1,
|
||||
@Headers String param2,
|
||||
MessageHeaders param3,
|
||||
MessageHeaderAccessor param4,
|
||||
TestMessageHeaderAccessor param5) {
|
||||
}
|
||||
|
||||
|
||||
public static class TestMessageHeaderAccessor extends NativeMessageHeaderAccessor {
|
||||
|
||||
TestMessageHeaderAccessor(Message<?> message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public static TestMessageHeaderAccessor wrap(Message<?> message) {
|
||||
return new TestMessageHeaderAccessor(message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user