001/* 002 * Copyright 2002-2017 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.web.client; 018 019import java.io.IOException; 020import java.lang.reflect.Type; 021import java.util.List; 022 023import org.apache.commons.logging.Log; 024import org.apache.commons.logging.LogFactory; 025 026import org.springframework.http.MediaType; 027import org.springframework.http.client.ClientHttpResponse; 028import org.springframework.http.converter.GenericHttpMessageConverter; 029import org.springframework.http.converter.HttpMessageConverter; 030import org.springframework.util.Assert; 031 032/** 033 * Response extractor that uses the given {@linkplain HttpMessageConverter entity converters} 034 * to convert the response into a type {@code T}. 035 * 036 * @author Arjen Poutsma 037 * @since 3.0 038 * @see RestTemplate 039 */ 040public class HttpMessageConverterExtractor<T> implements ResponseExtractor<T> { 041 042 private final Type responseType; 043 044 private final Class<T> responseClass; 045 046 private final List<HttpMessageConverter<?>> messageConverters; 047 048 private final Log logger; 049 050 051 /** 052 * Create a new instance of the {@code HttpMessageConverterExtractor} with the given response 053 * type and message converters. The given converters must support the response type. 054 */ 055 public HttpMessageConverterExtractor(Class<T> responseType, List<HttpMessageConverter<?>> messageConverters) { 056 this((Type) responseType, messageConverters); 057 } 058 059 /** 060 * Creates a new instance of the {@code HttpMessageConverterExtractor} with the given response 061 * type and message converters. The given converters must support the response type. 062 */ 063 public HttpMessageConverterExtractor(Type responseType, List<HttpMessageConverter<?>> messageConverters) { 064 this(responseType, messageConverters, LogFactory.getLog(HttpMessageConverterExtractor.class)); 065 } 066 067 @SuppressWarnings("unchecked") 068 HttpMessageConverterExtractor(Type responseType, List<HttpMessageConverter<?>> messageConverters, Log logger) { 069 Assert.notNull(responseType, "'responseType' must not be null"); 070 Assert.notEmpty(messageConverters, "'messageConverters' must not be empty"); 071 this.responseType = responseType; 072 this.responseClass = (responseType instanceof Class ? (Class<T>) responseType : null); 073 this.messageConverters = messageConverters; 074 this.logger = logger; 075 } 076 077 078 @Override 079 @SuppressWarnings({"unchecked", "rawtypes", "resource"}) 080 public T extractData(ClientHttpResponse response) throws IOException { 081 MessageBodyClientHttpResponseWrapper responseWrapper = new MessageBodyClientHttpResponseWrapper(response); 082 if (!responseWrapper.hasMessageBody() || responseWrapper.hasEmptyMessageBody()) { 083 return null; 084 } 085 MediaType contentType = getContentType(responseWrapper); 086 087 for (HttpMessageConverter<?> messageConverter : this.messageConverters) { 088 if (messageConverter instanceof GenericHttpMessageConverter) { 089 GenericHttpMessageConverter<?> genericMessageConverter = 090 (GenericHttpMessageConverter<?>) messageConverter; 091 if (genericMessageConverter.canRead(this.responseType, null, contentType)) { 092 if (logger.isDebugEnabled()) { 093 logger.debug("Reading [" + this.responseType + "] as \"" + 094 contentType + "\" using [" + messageConverter + "]"); 095 } 096 return (T) genericMessageConverter.read(this.responseType, null, responseWrapper); 097 } 098 } 099 if (this.responseClass != null) { 100 if (messageConverter.canRead(this.responseClass, contentType)) { 101 if (logger.isDebugEnabled()) { 102 logger.debug("Reading [" + this.responseClass.getName() + "] as \"" + 103 contentType + "\" using [" + messageConverter + "]"); 104 } 105 return (T) messageConverter.read((Class) this.responseClass, responseWrapper); 106 } 107 } 108 } 109 110 throw new RestClientException("Could not extract response: no suitable HttpMessageConverter found " + 111 "for response type [" + this.responseType + "] and content type [" + contentType + "]"); 112 } 113 114 private MediaType getContentType(ClientHttpResponse response) { 115 MediaType contentType = response.getHeaders().getContentType(); 116 if (contentType == null) { 117 if (logger.isTraceEnabled()) { 118 logger.trace("No Content-Type header found, defaulting to application/octet-stream"); 119 } 120 contentType = MediaType.APPLICATION_OCTET_STREAM; 121 } 122 return contentType; 123 } 124 125}