001/*
002 * Licensed under the Apache License, Version 2.0 (the "License");
003 * you may not use this file except in compliance with the License.
004 * You may obtain a copy of the License at
005 *
006 *     http://www.apache.org/licenses/LICENSE-2.0
007 *
008 * Unless required by applicable law or agreed to in writing, software
009 * distributed under the License is distributed on an "AS IS" BASIS,
010 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
011 * See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014package org.gbif.ws.client;
015
016import java.io.IOException;
017import java.lang.reflect.Type;
018import java.nio.charset.StandardCharsets;
019
020import org.springframework.http.HttpHeaders;
021import org.springframework.http.HttpStatus;
022import org.springframework.http.MediaType;
023import org.springframework.util.StringUtils;
024
025import com.fasterxml.jackson.databind.ObjectMapper;
026
027import feign.FeignException;
028import feign.Response;
029import feign.Util;
030import feign.codec.DecodeException;
031import feign.codec.Decoder;
032import feign.jackson.JacksonDecoder;
033
034public class ClientDecoder implements Decoder {
035
036  private final JacksonDecoder jacksonDecoder;
037
038  public ClientDecoder(ObjectMapper objectMapper) {
039    this.jacksonDecoder = new JacksonDecoder(objectMapper);
040  }
041
042  @Override
043  public Object decode(Response response, Type type) throws IOException, FeignException {
044    HttpStatus responseStatus = HttpStatus.resolve(response.status());
045
046    if (responseStatus == HttpStatus.NOT_FOUND || responseStatus == HttpStatus.NO_CONTENT) {
047      return null;
048    } else if (responseStatus != null && responseStatus.isError()) {
049      throw new DecodeException(response.status(), response.toString(), response.request());
050    }
051
052    MediaType contentType = getContentType(response);
053    if (MediaType.APPLICATION_JSON.equalsTypeAndSubtype(contentType)) {
054      return jacksonDecoder.decode(response, type);
055    } else if (MediaType.TEXT_PLAIN.equalsTypeAndSubtype(contentType)) {
056      return Util.toString(response.body().asReader(StandardCharsets.UTF_8));
057    } else if (MediaType.APPLICATION_OCTET_STREAM.equalsTypeAndSubtype(contentType)) {
058      return Util.toByteArray(response.body().asInputStream());
059    } else if (byte[].class.equals(type)) {
060      return Util.toByteArray(response.body().asInputStream());
061    } else {
062      throw new DecodeException(response.status(), "Unsupported response type", response.request());
063    }
064  }
065
066  /**
067   * Gets the first MediaType listed in the Content-Type header.
068   */
069  private static MediaType getContentType(Response response) {
070    return response.headers().get(HttpHeaders.CONTENT_TYPE).stream()
071        .findFirst()
072        .filter(StringUtils::hasLength)
073        .map(MediaType::parseMediaType)
074        .orElse(null);
075  }
076}