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