diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolver.java index 5ab96d8b478..d2a7fda9c44 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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,12 +16,19 @@ package org.springframework.messaging.handler.annotation.support; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.core.MethodParameter; import org.springframework.core.convert.ConversionService; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandlingException; import org.springframework.messaging.handler.annotation.Header; +import org.springframework.messaging.support.NativeMessageHeaderAccessor; +import org.springframework.util.MultiValueMap; + +import java.util.List; +import java.util.Map; /** * Resolves method parameters annotated with {@link Header @Header}. @@ -31,6 +38,8 @@ import org.springframework.messaging.handler.annotation.Header; */ public class HeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { + private static final Log logger = LogFactory.getLog(HeaderMethodArgumentResolver.class); + public HeaderMethodArgumentResolver(ConversionService cs, ConfigurableBeanFactory beanFactory) { super(cs, beanFactory); @@ -51,7 +60,39 @@ public class HeaderMethodArgumentResolver extends AbstractNamedValueMethodArgume protected Object resolveArgumentInternal(MethodParameter parameter, Message message, String name) throws Exception { - return message.getHeaders().get(name); + Object headerValue = message.getHeaders().get(name); + Object nativeHeaderValue = getNativeHeaderValue(message, name); + + if (headerValue != null && nativeHeaderValue != null) { + if (logger.isWarnEnabled()) { + logger.warn("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." ); + } + } + + return (headerValue != null) ? headerValue : nativeHeaderValue; + } + + private Object getNativeHeaderValue(Message message, String name) { + + Map> nativeHeaders = + (Map>) message.getHeaders().get(NativeMessageHeaderAccessor.NATIVE_HEADERS); + + if (name.startsWith("nativeHeaders.")) { + name = name.substring("nativeHeaders.".length()); + if (logger.isDebugEnabled()) { + logger.debug("Looking up native header '" + name + "'"); + } + } + + if ((nativeHeaders == null) || !nativeHeaders.containsKey(name)) { + return null; + } + + List nativeHeaderValues = nativeHeaders.get(name); + return (nativeHeaderValues.size() == 1) ? nativeHeaderValues.get(0) : nativeHeaderValues; } @Override diff --git a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolverTests.java b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolverTests.java index b919fe06a59..ba0be7f8482 100644 --- a/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolverTests.java +++ b/spring-messaging/src/test/java/org/springframework/messaging/handler/annotation/support/HeaderMethodArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -30,6 +30,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandlingException; import org.springframework.messaging.handler.annotation.Header; import org.springframework.messaging.support.MessageBuilder; +import org.springframework.messaging.support.NativeMessageHeaderAccessor; import static org.junit.Assert.*; @@ -47,6 +48,7 @@ public class HeaderMethodArgumentResolverTests { private MethodParameter paramNamedDefaultValueStringHeader; private MethodParameter paramSystemProperty; private MethodParameter paramNotAnnotated; + private MethodParameter paramNativeHeader; @Before @@ -57,11 +59,12 @@ public class HeaderMethodArgumentResolverTests { this.resolver = new HeaderMethodArgumentResolver(new DefaultConversionService(), cxt.getBeanFactory()); Method method = getClass().getDeclaredMethod("handleMessage", - String.class, String.class, String.class, String.class); + String.class, String.class, String.class, String.class, String.class); this.paramRequired = new MethodParameter(method, 0); this.paramNamedDefaultValueStringHeader = new MethodParameter(method, 1); this.paramSystemProperty = new MethodParameter(method, 2); this.paramNotAnnotated = new MethodParameter(method, 3); + this.paramNativeHeader = new MethodParameter(method, 4); this.paramRequired.initParameterNameDiscovery(new DefaultParameterNameDiscoverer()); GenericTypeResolver.resolveParameterType(this.paramRequired, HeaderMethodArgumentResolver.class); @@ -76,7 +79,33 @@ public class HeaderMethodArgumentResolverTests { @Test public void resolveArgument() throws Exception { Message message = MessageBuilder.withPayload(new byte[0]).setHeader("param1", "foo").build(); - this.resolver.resolveArgument(this.paramRequired, message); + Object result = this.resolver.resolveArgument(this.paramRequired, message); + + assertEquals("foo", result); + } + + // SPR-11326 + + @Test + public void resolveArgumentNativeHeader() throws Exception { + + TestMessageHeaderAccessor headers = new TestMessageHeaderAccessor(); + headers.setNativeHeader("param1", "foo"); + Message message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build(); + + assertEquals("foo", this.resolver.resolveArgument(this.paramRequired, message)); + } + + @Test + public void resolveArgumentNativeHeaderAmbiguity() throws Exception { + + TestMessageHeaderAccessor headers = new TestMessageHeaderAccessor(); + headers.setHeader("param1", "foo"); + headers.setNativeHeader("param1", "native-foo"); + Message 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)); } @Test(expected = MessageHandlingException.class) @@ -112,7 +141,16 @@ public class HeaderMethodArgumentResolverTests { @Header String param1, @Header(value = "name", defaultValue = "bar") String param2, @Header(value = "name", defaultValue="#{systemProperties.systemProperty}") String param3, - String param4) { + String param4, + @Header("nativeHeaders.param1") String nativeHeaderParam1) { + } + + + public static class TestMessageHeaderAccessor extends NativeMessageHeaderAccessor { + + protected TestMessageHeaderAccessor() { + super((Message) null); + } } }