001/*
002 * Copyright 2002-2014 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.messaging.handler.annotation.support;
018
019import java.util.List;
020import java.util.Map;
021
022import org.apache.commons.logging.Log;
023import org.apache.commons.logging.LogFactory;
024
025import org.springframework.beans.factory.config.ConfigurableBeanFactory;
026import org.springframework.core.MethodParameter;
027import org.springframework.core.convert.ConversionService;
028import org.springframework.messaging.Message;
029import org.springframework.messaging.MessageHandlingException;
030import org.springframework.messaging.handler.annotation.Header;
031import org.springframework.messaging.support.NativeMessageHeaderAccessor;
032
033/**
034 * Resolves method parameters annotated with {@link Header @Header}.
035 *
036 * @author Rossen Stoyanchev
037 * @since 4.0
038 */
039public class HeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver {
040
041        private static final Log logger = LogFactory.getLog(HeaderMethodArgumentResolver.class);
042
043
044        public HeaderMethodArgumentResolver(ConversionService cs, ConfigurableBeanFactory beanFactory) {
045                super(cs, beanFactory);
046        }
047
048
049        @Override
050        public boolean supportsParameter(MethodParameter parameter) {
051                return parameter.hasParameterAnnotation(Header.class);
052        }
053
054        @Override
055        protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
056                Header annotation = parameter.getParameterAnnotation(Header.class);
057                return new HeaderNamedValueInfo(annotation);
058        }
059
060        @Override
061        protected Object resolveArgumentInternal(MethodParameter parameter, Message<?> message, String name)
062                        throws Exception {
063
064                Object headerValue = message.getHeaders().get(name);
065                Object nativeHeaderValue = getNativeHeaderValue(message, name);
066
067                if (headerValue != null && nativeHeaderValue != null) {
068                        if (logger.isWarnEnabled()) {
069                                logger.warn("Message headers contain two values for the same header '" + name + "', " +
070                                                "one in the top level header map and a second in the nested map with native headers. " +
071                                                "Using the value from top level map. " +
072                                                "Use 'nativeHeader.myHeader' to resolve to the value from the nested native header map." );
073                        }
074                }
075
076                return (headerValue != null ? headerValue : nativeHeaderValue);
077        }
078
079        private Object getNativeHeaderValue(Message<?> message, String name) {
080                Map<String, List<String>> nativeHeaders = getNativeHeaders(message);
081                if (name.startsWith("nativeHeaders.")) {
082                        name = name.substring("nativeHeaders.".length());
083                }
084                if (nativeHeaders == null || !nativeHeaders.containsKey(name)) {
085                        return null;
086                }
087                List<?> nativeHeaderValues = nativeHeaders.get(name);
088                return (nativeHeaderValues.size() == 1 ? nativeHeaderValues.get(0) : nativeHeaderValues);
089        }
090
091        @SuppressWarnings("unchecked")
092        private Map<String, List<String>> getNativeHeaders(Message<?> message) {
093                return (Map<String, List<String>>) message.getHeaders().get(
094                                NativeMessageHeaderAccessor.NATIVE_HEADERS);
095        }
096
097        @Override
098        protected void handleMissingValue(String headerName, MethodParameter parameter, Message<?> message) {
099                throw new MessageHandlingException(message, "Missing header '" + headerName +
100                                "' for method parameter type [" + parameter.getParameterType() + "]");
101        }
102
103
104        private static class HeaderNamedValueInfo extends NamedValueInfo {
105
106                private HeaderNamedValueInfo(Header annotation) {
107                        super(annotation.name(), annotation.required(), annotation.defaultValue());
108                }
109        }
110
111}