From 168f372dd4770f7b5cec8aaf01cadcc65df7d617 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Wed, 3 May 2017 16:24:14 -0400 Subject: [PATCH] Make `DefaultHttpHeaderMapper` Easier to Subclass It's difficult to subclass this mapper (e.g. to extend `toHeaders` to tweak the map) because the factory methods for inbound and outbound mappers use private static fields to set up the standard header mapping. Extract that logic so that subclasses can create similar static methods and invoke the setup code to set the standard header mapping. Polishing: make more `DefaultHttpHeaderMapper` members available for inheritors --- .../http/support/DefaultHttpHeaderMapper.java | 197 ++++++++++-------- 1 file changed, 108 insertions(+), 89 deletions(-) diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java b/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java index d6ea869c59..d1b1158b22 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java @@ -73,110 +73,109 @@ import org.springframework.util.StringUtils; */ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanFactoryAware, InitializingBean { - private static final Log logger = LogFactory.getLog(DefaultHttpHeaderMapper.class); + protected final Log logger = LogFactory.getLog(getClass()); + public static final String ACCEPT = "Accept"; - private static final String ACCEPT = "Accept"; + public static final String ACCEPT_CHARSET = "Accept-Charset"; - private static final String ACCEPT_CHARSET = "Accept-Charset"; + public static final String ACCEPT_ENCODING = "Accept-Encoding"; - private static final String ACCEPT_ENCODING = "Accept-Encoding"; + public static final String ACCEPT_LANGUAGE = "Accept-Language"; - private static final String ACCEPT_LANGUAGE = "Accept-Language"; + public static final String ACCEPT_RANGES = "Accept-Ranges"; - private static final String ACCEPT_RANGES = "Accept-Ranges"; + public static final String AGE = "Age"; - private static final String AGE = "Age"; + public static final String ALLOW = "Allow"; - private static final String ALLOW = "Allow"; + public static final String AUTHORIZATION = "Authorization"; - private static final String AUTHORIZATION = "Authorization"; + public static final String CACHE_CONTROL = "Cache-Control"; - private static final String CACHE_CONTROL = "Cache-Control"; + public static final String CONNECTION = "Connection"; - private static final String CONNECTION = "Connection"; + public static final String CONTENT_ENCODING = "Content-Encoding"; - private static final String CONTENT_ENCODING = "Content-Encoding"; + public static final String CONTENT_LANGUAGE = "Content-Language"; - private static final String CONTENT_LANGUAGE = "Content-Language"; + public static final String CONTENT_LENGTH = "Content-Length"; - private static final String CONTENT_LENGTH = "Content-Length"; + public static final String CONTENT_LOCATION = "Content-Location"; - private static final String CONTENT_LOCATION = "Content-Location"; + public static final String CONTENT_MD5 = "Content-MD5"; - private static final String CONTENT_MD5 = "Content-MD5"; + public static final String CONTENT_RANGE = "Content-Range"; - private static final String CONTENT_RANGE = "Content-Range"; + public static final String CONTENT_TYPE = "Content-Type"; - private static final String CONTENT_TYPE = "Content-Type"; - - private static final String CONTENT_DISPOSITION = "Content-Disposition"; + public static final String CONTENT_DISPOSITION = "Content-Disposition"; public static final String COOKIE = "Cookie"; - private static final String DATE = "Date"; + public static final String DATE = "Date"; - private static final String ETAG = "ETag"; + public static final String ETAG = "ETag"; - private static final String EXPECT = "Expect"; + public static final String EXPECT = "Expect"; - private static final String EXPIRES = "Expires"; + public static final String EXPIRES = "Expires"; - private static final String FROM = "From"; + public static final String FROM = "From"; - private static final String HOST = "Host"; + public static final String HOST = "Host"; - private static final String IF_MATCH = "If-Match"; + public static final String IF_MATCH = "If-Match"; - private static final String IF_MODIFIED_SINCE = "If-Modified-Since"; + public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; - private static final String IF_NONE_MATCH = "If-None-Match"; + public static final String IF_NONE_MATCH = "If-None-Match"; - private static final String IF_RANGE = "If-Range"; + public static final String IF_RANGE = "If-Range"; - private static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; + public static final String IF_UNMODIFIED_SINCE = "If-Unmodified-Since"; - private static final String LAST_MODIFIED = "Last-Modified"; + public static final String LAST_MODIFIED = "Last-Modified"; - private static final String LOCATION = "Location"; + public static final String LOCATION = "Location"; - private static final String MAX_FORWARDS = "Max-Forwards"; + public static final String MAX_FORWARDS = "Max-Forwards"; - private static final String PRAGMA = "Pragma"; + public static final String PRAGMA = "Pragma"; - private static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; + public static final String PROXY_AUTHENTICATE = "Proxy-Authenticate"; - private static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; + public static final String PROXY_AUTHORIZATION = "Proxy-Authorization"; - private static final String RANGE = "Range"; + public static final String RANGE = "Range"; - private static final String REFERER = "Referer"; + public static final String REFERER = "Referer"; - private static final String REFRESH = "Refresh"; + public static final String REFRESH = "Refresh"; - private static final String RETRY_AFTER = "Retry-After"; + public static final String RETRY_AFTER = "Retry-After"; - private static final String SERVER = "Server"; + public static final String SERVER = "Server"; public static final String SET_COOKIE = "Set-Cookie"; - private static final String TE = "TE"; + public static final String TE = "TE"; - private static final String TRAILER = "Trailer"; + public static final String TRAILER = "Trailer"; - private static final String UPGRADE = "Upgrade"; + public static final String UPGRADE = "Upgrade"; - private static final String USER_AGENT = "User-Agent"; + public static final String USER_AGENT = "User-Agent"; - private static final String VARY = "Vary"; + public static final String VARY = "Vary"; - private static final String VIA = "Via"; + public static final String VIA = "Via"; - private static final String WARNING = "Warning"; + public static final String WARNING = "Warning"; - private static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; - private static final String TRANSFER_ENCODING = "Transfer-Encoding"; + public static final String TRANSFER_ENCODING = "Transfer-Encoding"; private static final String[] HTTP_REQUEST_HEADER_NAMES = new String[] { ACCEPT, @@ -259,7 +258,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF public static final String HTTP_RESPONSE_HEADER_NAME_PATTERN = "HTTP_RESPONSE_HEADERS"; // Copy of 'org.springframework.http.HttpHeaders#DATE_FORMATS' - private static final DateTimeFormatter[] DATE_FORMATS = new DateTimeFormatter[] { + protected static final DateTimeFormatter[] DATE_FORMATS = new DateTimeFormatter[] { DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss zzz", Locale.US).withZone(ZoneId.of("GMT")), DateTimeFormatter.ofPattern("EEE, dd-MMM-yy HH:mm:ss zzz", Locale.US).withZone(ZoneId.of("GMT")), DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss yyyy", Locale.US).withZone(ZoneId.of("GMT")) @@ -301,6 +300,10 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF this.beanFactory = beanFactory; } + protected BeanFactory getBeanFactory() { + return this.beanFactory; + } + /** * Provide the header names that should be mapped to an HTTP request (for outbound adapters) * or HTTP response (for inbound adapters) from a Spring Integration Message's headers. @@ -400,8 +403,8 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF */ @Override public void fromHeaders(MessageHeaders headers, HttpHeaders target) { - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("outboundHeaderNames={0}", + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("outboundHeaderNames={0}", CollectionUtils.arrayToList(this.outboundHeaderNames))); } for (Entry entry : headers.entrySet()) { @@ -418,8 +421,8 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF name = StringUtils.startsWithIgnoreCase(name, this.userDefinedHeaderPrefix) ? name : this.userDefinedHeaderPrefix + name; } - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("setting headerName=[{0}], value={1}", name, value)); + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("setting headerName=[{0}], value={1}", name, value)); } this.setHttpHeader(target, name, value); } @@ -434,8 +437,8 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF */ @Override public Map toHeaders(HttpHeaders source) { - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("inboundHeaderNames={0}", + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("inboundHeaderNames={0}", CollectionUtils.arrayToList(this.inboundHeaderNames))); } Map target = new HashMap(); @@ -452,8 +455,8 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF ? this.getHttpHeader(source, prefixedName) : this.getHttpHeader(source, name); if (value != null) { - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("setting headerName=[{0}], value={1}", name, value)); + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("setting headerName=[{0}], value={1}", name, value)); } this.setMessageHeader(target, name, value); } @@ -461,8 +464,8 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF else { Object value = this.getHttpHeader(source, name); if (value != null) { - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("setting headerName=[{0}], value={1}", name, value)); + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("setting headerName=[{0}], value={1}", name, value)); } if (CONTENT_TYPE.equalsIgnoreCase(name)) { name = MessageHeaders.CONTENT_TYPE; @@ -482,7 +485,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF } } - private boolean containsElementIgnoreCase(String[] headerNames, String name) { + protected final boolean containsElementIgnoreCase(String[] headerNames, String name) { for (String headerName : headerNames) { if (headerName.equalsIgnoreCase(name)) { return true; @@ -500,8 +503,8 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF * mapping of exclusions for specific headers. */ if (this.containsElementIgnoreCase(this.excludedInboundStandardResponseHeaderNames, headerName)) { - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped (excluded)", headerName)); + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped (excluded)", headerName)); } return false; } @@ -513,8 +516,8 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF * mapping of exclusions for specific headers. */ if (this.containsElementIgnoreCase(this.excludedOutboundStandardRequestHeaderNames, headerName)) { - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped (excluded)", headerName)); + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped (excluded)", headerName)); } return false; } @@ -522,7 +525,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF return this.shouldMapHeader(headerName, outboundHeaderNamesLower); } - private boolean shouldMapInboundHeader(String headerName) { + protected final boolean shouldMapInboundHeader(String headerName) { return this.shouldMapHeader(headerName, this.inboundHeaderNamesLower); } @@ -535,32 +538,32 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF if (patterns != null && patterns.length > 0) { for (String pattern : patterns) { if (PatternMatchUtils.simpleMatch(pattern, headerName)) { - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("headerName=[{0}] WILL be mapped, matched pattern={1}", + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("headerName=[{0}] WILL be mapped, matched pattern={1}", headerName, pattern)); } return true; } else if (HTTP_REQUEST_HEADER_NAME_PATTERN.equals(pattern) && HTTP_REQUEST_HEADER_NAMES_LOWER.contains(headerName)) { - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("headerName=[{0}] WILL be mapped, matched pattern={1}", + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("headerName=[{0}] WILL be mapped, matched pattern={1}", headerName, pattern)); } return true; } else if (HTTP_RESPONSE_HEADER_NAME_PATTERN.equals(pattern) && HTTP_RESPONSE_HEADER_NAMES_LOWER.contains(headerName)) { - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("headerName=[{0}] WILL be mapped, matched pattern={1}", + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("headerName=[{0}] WILL be mapped, matched pattern={1}", headerName, pattern)); } return true; } } } - if (logger.isDebugEnabled()) { - logger.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped", headerName)); + if (this.logger.isDebugEnabled()) { + this.logger.debug(MessageFormat.format("headerName=[{0}] WILL NOT be mapped", headerName)); } return false; } @@ -938,7 +941,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF target.add(name, convertedValue); } else { - logger.warn("Element of the header '" + name + "' with value '" + value + + this.logger.warn("Element of the header '" + name + "' with value '" + value + "' will not be set since it is not a String and no Converter is available. " + "Consider registering a Converter with ConversionService (e.g., )"); } @@ -950,14 +953,14 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF target.set(name, convertedValue); } else { - logger.warn("Header '" + name + "' with value '" + value + + this.logger.warn("Header '" + name + "' with value '" + value + "' will not be set since it is not a String and no Converter is available. " + "Consider registering a Converter with ConversionService (e.g., )"); } } } - private Object getHttpHeader(HttpHeaders source, String name) { + protected Object getHttpHeader(HttpHeaders source, String name) { if (ACCEPT.equalsIgnoreCase(name)) { return source.getAccept(); } @@ -992,9 +995,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF return (expires > -1) ? expires : null; } catch (Exception e) { - if (logger.isDebugEnabled()) { - logger.debug(e.getMessage()); - } + this.logger.debug(e.getMessage()); // According to RFC 2616 return null; } @@ -1052,7 +1053,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF } } - private String convertToString(Object value) { + protected String convertToString(Object value) { if (this.conversionService != null && this.conversionService.canConvert(TypeDescriptor.forObject(value), TypeDescriptor.valueOf(String.class))) { @@ -1063,7 +1064,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF // Utility methods - private long getFirstDate(String headerValue, String headerName) { + protected long getFirstDate(String headerValue, String headerName) { for (DateTimeFormatter dateFormat : DATE_FORMATS) { try { return dateFormat.parse(headerValue, ZonedDateTime::from) @@ -1078,7 +1079,7 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF + "' header"); } - private String formatDate(long date) { + protected String formatDate(long date) { return DATE_FORMATS[0].format(Instant.ofEpochMilli(date)); } @@ -1090,10 +1091,19 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF */ public static DefaultHttpHeaderMapper outboundMapper() { DefaultHttpHeaderMapper mapper = new DefaultHttpHeaderMapper(); + setupDefaultOutboundMapper(mapper); + return mapper; + } + + /** + * Subclasses can call this from a static outboundMapper() method to set up + * standard header mappings for an outbound mapper. + * @param mapper the mapper. + */ + protected static void setupDefaultOutboundMapper(DefaultHttpHeaderMapper mapper) { mapper.setOutboundHeaderNames(HTTP_REQUEST_HEADER_NAMES); mapper.setInboundHeaderNames(HTTP_RESPONSE_HEADER_NAMES); mapper.setExcludedOutboundStandardRequestHeaderNames(HTTP_REQUEST_HEADER_NAMES_OUTBOUND_EXCLUSIONS); - return mapper; } /** @@ -1104,10 +1114,19 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF */ public static DefaultHttpHeaderMapper inboundMapper() { DefaultHttpHeaderMapper mapper = new DefaultHttpHeaderMapper(); - mapper.setInboundHeaderNames(HTTP_REQUEST_HEADER_NAMES); - mapper.setOutboundHeaderNames(HTTP_RESPONSE_HEADER_NAMES); - mapper.setExcludedInboundStandardResponseHeaderNames(HTTP_RESPONSE_HEADER_NAMES_INBOUND_EXCLUSIONS); + setupDefaultInboundMapper(mapper); return mapper; } + /** + * Subclasses can call this from a static inboundMapper() method to set up + * standard header mappings for an inbound mapper. + * @param mapper the mapper. + */ + protected static void setupDefaultInboundMapper(DefaultHttpHeaderMapper mapper) { + mapper.setInboundHeaderNames(HTTP_REQUEST_HEADER_NAMES); + mapper.setOutboundHeaderNames(HTTP_RESPONSE_HEADER_NAMES); + mapper.setExcludedInboundStandardResponseHeaderNames(HTTP_RESPONSE_HEADER_NAMES_INBOUND_EXCLUSIONS); + } + }