Added the @Headers parameter-level annotation for mapping a java.util.Map (with String-typed keys) to/from the MessageHeaders in MethodParameterMessageMapper.
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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.integration.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;
|
||||
|
||||
/**
|
||||
* Annotation indicating that a method parameter's value should be mapped to or
|
||||
* from the message headers. The annotated parameter must be assignable to
|
||||
* {@link java.util.Map}, and all of the Map's keys must be Strings.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface Headers {
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.integration.message;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
@@ -26,6 +27,7 @@ import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterNameDiscoverer;
|
||||
import org.springframework.integration.annotation.Header;
|
||||
import org.springframework.integration.annotation.Headers;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -70,6 +72,7 @@ public class MethodParameterMessageMapper implements MessageMapper<Object[]> {
|
||||
Assert.isTrue(parameters.length == this.parameterMetadata.length,
|
||||
"wrong number of parameters: expected " + this.parameterMetadata.length
|
||||
+ ", received " + parameters.length);
|
||||
Message<?> message = null;
|
||||
Object payload = null;
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
@@ -77,18 +80,33 @@ public class MethodParameterMessageMapper implements MessageMapper<Object[]> {
|
||||
MethodParameterMetadata metadata = this.parameterMetadata[i];
|
||||
Class<?> expectedType = metadata.type;
|
||||
if (expectedType.equals(Header.class)) {
|
||||
if (metadata.required) {
|
||||
Assert.notNull(value, "header '" + metadata.key + "' is required");
|
||||
if (value != null) {
|
||||
headers.put(metadata.key, value);
|
||||
}
|
||||
else {
|
||||
Assert.isTrue(!metadata.required, "header '" + metadata.key + "' is required");
|
||||
}
|
||||
headers.put(metadata.key, value);
|
||||
}
|
||||
// TODO: add checks for Map and Properties to be used as headers
|
||||
else if (expectedType.equals(Headers.class)) {
|
||||
if (value != null) {
|
||||
this.addHeadersAnnotatedParameterToMap(value, headers);
|
||||
}
|
||||
}
|
||||
else if (expectedType.equals(Message.class)) {
|
||||
Assert.isNull(message, "more than one Message argument received");
|
||||
message = (Message<?>) value;
|
||||
}
|
||||
else {
|
||||
Assert.isNull(payload, "unable to determine a single payload object, found: "
|
||||
+ "[" + payload + "] and [" + value + "]s");
|
||||
payload = value;
|
||||
}
|
||||
}
|
||||
if (message != null) {
|
||||
Assert.isNull(payload, "cannot handle payload object [" + payload
|
||||
+ "] since a Message-typed parameter has also been provided");
|
||||
return MessageBuilder.fromMessage(message).copyHeadersIfAbsent(headers).build();
|
||||
}
|
||||
Assert.notNull(payload, "payload object must not be null");
|
||||
return MessageBuilder.withPayload(payload).copyHeaders(headers).build();
|
||||
}
|
||||
@@ -142,15 +160,19 @@ public class MethodParameterMessageMapper implements MessageMapper<Object[]> {
|
||||
methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
|
||||
GenericTypeResolver.resolveParameterType(methodParam, this.method.getDeclaringClass());
|
||||
Object[] paramAnnotations = methodParam.getParameterAnnotations();
|
||||
String headerName = null;
|
||||
for (int j = 0; j < paramAnnotations.length; j++) {
|
||||
if (Header.class.isInstance(paramAnnotations[j])) {
|
||||
Header headerAnnotation = (Header) paramAnnotations[j];
|
||||
headerName = this.resolveParameterNameIfNecessary(headerAnnotation.value(), methodParam);
|
||||
String headerName = this.resolveParameterNameIfNecessary(headerAnnotation.value(), methodParam);
|
||||
parameterMetadata[i] = new MethodParameterMetadata(Header.class, headerName, headerAnnotation.required());
|
||||
}
|
||||
else if (Headers.class.isInstance(paramAnnotations[j])) {
|
||||
Assert.isAssignable(Map.class, methodParam.getParameterType(),
|
||||
"parameter with the @Headers annotation must be assignable to java.util.Map");
|
||||
parameterMetadata[i] = new MethodParameterMetadata(Headers.class, null, false);
|
||||
}
|
||||
}
|
||||
if (headerName == null) {
|
||||
if (parameterMetadata[i] == null) {
|
||||
parameterMetadata[i] = new MethodParameterMetadata(methodParam.getParameterType(), null, false);
|
||||
}
|
||||
}
|
||||
@@ -178,6 +200,17 @@ public class MethodParameterMessageMapper implements MessageMapper<Object[]> {
|
||||
return paramName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void addHeadersAnnotatedParameterToMap(Object value, Map<String, Object> headers) {
|
||||
Map map = (Map) value;
|
||||
for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) iter.next();
|
||||
Assert.isTrue(entry.getKey() instanceof String,
|
||||
"Map annotated with @Headers must have String-typed keys");
|
||||
headers.put((String) entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MethodParameterMetadata {
|
||||
|
||||
|
||||
@@ -20,10 +20,13 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.integration.annotation.Header;
|
||||
import org.springframework.integration.annotation.Headers;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
@@ -90,6 +93,86 @@ public class MethodParameterMessageMapperToMessageTests {
|
||||
assertNull(message.getHeaders().get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageWithPayloadAndHeadersMap() throws Exception {
|
||||
Method method = TestService.class.getMethod(
|
||||
"sendPayloadAndHeadersMap", String.class, Map.class);
|
||||
MethodParameterMessageMapper mapper = new MethodParameterMessageMapper(method);
|
||||
Map<String, Object> headers = new HashMap<String, Object>();
|
||||
headers.put("abc", 123);
|
||||
headers.put("def", 456);
|
||||
Message<?> message = mapper.toMessage(new Object[] { "test", headers });
|
||||
assertEquals("test", message.getPayload());
|
||||
assertEquals(123, message.getHeaders().get("abc"));
|
||||
assertEquals(456, message.getHeaders().get("def"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageWithPayloadAndNullHeadersMap() throws Exception {
|
||||
Method method = TestService.class.getMethod(
|
||||
"sendPayloadAndHeadersMap", String.class, Map.class);
|
||||
MethodParameterMessageMapper mapper = new MethodParameterMessageMapper(method);
|
||||
Message<?> message = mapper.toMessage(new Object[] { "test", null });
|
||||
assertEquals("test", message.getPayload());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void toMessageWithPayloadAndHeadersMapWithNonStringKey() throws Exception {
|
||||
Method method = TestService.class.getMethod(
|
||||
"sendPayloadAndHeadersMap", String.class, Map.class);
|
||||
MethodParameterMessageMapper mapper = new MethodParameterMessageMapper(method);
|
||||
Map<Integer, String> headers = new HashMap<Integer, String>();
|
||||
headers.put(123, "abc");
|
||||
mapper.toMessage(new Object[] { "test", headers });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageWithMessageParameter() throws Exception {
|
||||
Method method = TestService.class.getMethod("sendMessage", Message.class);
|
||||
MethodParameterMessageMapper mapper = new MethodParameterMessageMapper(method);
|
||||
Message<?> inputMessage = MessageBuilder.withPayload("test message").build();
|
||||
Message<?> message = mapper.toMessage(new Object[] { inputMessage });
|
||||
assertEquals("test message", message.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageWithMessageParameterAndHeader() throws Exception {
|
||||
Method method = TestService.class.getMethod("sendMessageAndHeader", Message.class, String.class);
|
||||
MethodParameterMessageMapper mapper = new MethodParameterMessageMapper(method);
|
||||
Message<?> inputMessage = MessageBuilder.withPayload("test message").build();
|
||||
Message<?> message = mapper.toMessage(new Object[] { inputMessage, "bar" });
|
||||
assertEquals("test message", message.getPayload());
|
||||
assertEquals("bar", message.getHeaders().get("foo"));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void toMessageWithMessageParameterAndRequiredHeaderButNullValue() throws Exception {
|
||||
Method method = TestService.class.getMethod("sendMessageAndHeader", Message.class, String.class);
|
||||
MethodParameterMessageMapper mapper = new MethodParameterMessageMapper(method);
|
||||
Message<?> inputMessage = MessageBuilder.withPayload("test message").build();
|
||||
mapper.toMessage(new Object[] { inputMessage, null });
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageWithMessageParameterAndOptionalHeaderWithValue() throws Exception {
|
||||
Method method = TestService.class.getMethod("sendMessageAndOptionalHeader", Message.class, String.class);
|
||||
MethodParameterMessageMapper mapper = new MethodParameterMessageMapper(method);
|
||||
Message<?> inputMessage = MessageBuilder.withPayload("test message").build();
|
||||
Message<?> message = mapper.toMessage(new Object[] { inputMessage, "bar" });
|
||||
assertEquals("test message", message.getPayload());
|
||||
assertEquals("bar", message.getHeaders().get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void toMessageWithMessageParameterAndOptionalHeaderWithNull() throws Exception {
|
||||
Method method = TestService.class.getMethod("sendMessageAndOptionalHeader", Message.class, String.class);
|
||||
MethodParameterMessageMapper mapper = new MethodParameterMessageMapper(method);
|
||||
Message<?> inputMessage = MessageBuilder.withPayload("test message").build();
|
||||
Message<?> message = mapper.toMessage(new Object[] { inputMessage, null });
|
||||
assertEquals("test message", message.getPayload());
|
||||
assertNull(message.getHeaders().get("foo"));
|
||||
}
|
||||
|
||||
|
||||
private static interface TestService {
|
||||
|
||||
@@ -99,6 +182,14 @@ public class MethodParameterMessageMapperToMessageTests {
|
||||
|
||||
void sendPayloadAndOptionalHeader(String payload, @Header(value="foo", required=false) String foo);
|
||||
|
||||
void sendPayloadAndHeadersMap(String payload, @Headers Map<String, Object> headers);
|
||||
|
||||
void sendMessage(Message<?> message);
|
||||
|
||||
void sendMessageAndHeader(Message<?> message, @Header("foo") String foo);
|
||||
|
||||
void sendMessageAndOptionalHeader(Message<?> message, @Header(value="foo", required=false) String foo);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user