001/* 002 * Copyright 2002-2019 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.lang.Nullable; 029import org.springframework.messaging.Message; 030import org.springframework.messaging.MessageHandlingException; 031import org.springframework.messaging.handler.annotation.Header; 032import org.springframework.messaging.support.NativeMessageHeaderAccessor; 033import org.springframework.util.Assert; 034 035/** 036 * Resolver for {@link Header @Header} arguments. Headers are resolved from 037 * either the top-level header map or the nested 038 * {@link NativeMessageHeaderAccessor native} header map. 039 * 040 * @author Rossen Stoyanchev 041 * @since 4.0 042 * 043 * @see HeadersMethodArgumentResolver 044 * @see NativeMessageHeaderAccessor 045 */ 046public class HeaderMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver { 047 048 private static final Log logger = LogFactory.getLog(HeaderMethodArgumentResolver.class); 049 050 051 public HeaderMethodArgumentResolver( 052 ConversionService conversionService, @Nullable ConfigurableBeanFactory beanFactory) { 053 054 super(conversionService, beanFactory); 055 } 056 057 058 @Override 059 public boolean supportsParameter(MethodParameter parameter) { 060 return parameter.hasParameterAnnotation(Header.class); 061 } 062 063 @Override 064 protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) { 065 Header annot = parameter.getParameterAnnotation(Header.class); 066 Assert.state(annot != null, "No Header annotation"); 067 return new HeaderNamedValueInfo(annot); 068 } 069 070 @Override 071 @Nullable 072 protected Object resolveArgumentInternal(MethodParameter parameter, Message<?> message, String name) 073 throws Exception { 074 075 Object headerValue = message.getHeaders().get(name); 076 Object nativeHeaderValue = getNativeHeaderValue(message, name); 077 078 if (headerValue != null && nativeHeaderValue != null) { 079 if (logger.isDebugEnabled()) { 080 logger.debug("A value was found for '" + name + "', in both the top level header map " + 081 "and also in the nested map for native headers. Using the value from top level map. " + 082 "Use 'nativeHeader.myHeader' to resolve the native header."); 083 } 084 } 085 086 return (headerValue != null ? headerValue : nativeHeaderValue); 087 } 088 089 @Nullable 090 private Object getNativeHeaderValue(Message<?> message, String name) { 091 Map<String, List<String>> nativeHeaders = getNativeHeaders(message); 092 if (name.startsWith("nativeHeaders.")) { 093 name = name.substring("nativeHeaders.".length()); 094 } 095 if (nativeHeaders == null || !nativeHeaders.containsKey(name)) { 096 return null; 097 } 098 List<?> nativeHeaderValues = nativeHeaders.get(name); 099 return (nativeHeaderValues.size() == 1 ? nativeHeaderValues.get(0) : nativeHeaderValues); 100 } 101 102 @SuppressWarnings("unchecked") 103 @Nullable 104 private Map<String, List<String>> getNativeHeaders(Message<?> message) { 105 return (Map<String, List<String>>) message.getHeaders().get(NativeMessageHeaderAccessor.NATIVE_HEADERS); 106 } 107 108 @Override 109 protected void handleMissingValue(String headerName, MethodParameter parameter, Message<?> message) { 110 throw new MessageHandlingException(message, "Missing header '" + headerName + 111 "' for method parameter type [" + parameter.getParameterType() + "]"); 112 } 113 114 115 private static final class HeaderNamedValueInfo extends NamedValueInfo { 116 117 private HeaderNamedValueInfo(Header annotation) { 118 super(annotation.name(), annotation.required(), annotation.defaultValue()); 119 } 120 } 121 122}