RestApiExceptionParserImpl.java

/*
 * Copyright 2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.bremersee.exception;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.util.Collection;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.bremersee.exception.model.RestApiException;
import org.bremersee.http.HttpHeadersHelper;
import org.bremersee.http.MediaTypeHelper;
import org.springframework.http.HttpHeaders;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

/**
 * The default implementation of a http response parser that creates a {@link RestApiException}.
 *
 * @author Christian Bremer
 */
@Slf4j
public class RestApiExceptionParserImpl implements RestApiExceptionParser {

  private final ObjectMapper objectMapper;

  private final XmlMapper xmlMapper;

  public RestApiExceptionParserImpl() {
    this.objectMapper = Jackson2ObjectMapperBuilder.json().build();
    this.xmlMapper = Jackson2ObjectMapperBuilder.xml().createXmlMapper(true).build();
  }

  /**
   * Instantiates a new rest api exception parser.
   *
   * @param objectMapperBuilder the object mapper builder
   */
  @SuppressWarnings("unused")
  public RestApiExceptionParserImpl(Jackson2ObjectMapperBuilder objectMapperBuilder) {
    this.objectMapper = objectMapperBuilder.build();
    this.xmlMapper = objectMapperBuilder.createXmlMapper(true).build();
  }

  @SuppressWarnings("unused")
  public RestApiExceptionParserImpl(
      ObjectMapper objectMapper,
      XmlMapper xmlMapper) {
    this.objectMapper = objectMapper;
    this.xmlMapper = xmlMapper;
  }

  private ObjectMapper getJsonMapper() {
    return objectMapper;
  }

  private XmlMapper getXmlMapper() {
    return xmlMapper;
  }

  @Override
  public RestApiException parseException(
      @Nullable final String response,
      @Nullable final Map<String, ? extends Collection<String>> headers) {

    final HttpHeaders httpHeaders = HttpHeadersHelper.buildHttpHeaders(headers);
    final String contentType = String.valueOf(httpHeaders.getContentType());

    RestApiException restApiException = null;
    try {
      if (StringUtils.hasText(response) && MediaTypeHelper.canContentTypeBeJson(contentType)) {
        restApiException = getJsonMapper().readValue(response, RestApiException.class);
      }
    } catch (Exception ignored) {
      log.info("msg=[Response is not a 'RestApiException' as JSON.]");
    }
    try {
      if (restApiException == null
          && StringUtils.hasText(response)
          && MediaTypeHelper.canContentTypeBeXml(contentType)) {
        restApiException = getXmlMapper().readValue(response, RestApiException.class);
      }
    } catch (Exception ignored) {
      log.debug("msg=[Response is not a 'RestApiException' as XML.]");
    }
    if (restApiException == null) {
      restApiException = new RestApiException();

      final String id = httpHeaders.getFirst(RestApiExceptionUtils.ID_HEADER_NAME);
      if (StringUtils.hasText(id) && !RestApiExceptionUtils.NO_ID_VALUE.equals(id)) {
        restApiException.setId(id);
      }

      final String timestamp = httpHeaders.getFirst(RestApiExceptionUtils.TIMESTAMP_HEADER_NAME);
      restApiException.setTimestamp(RestApiExceptionUtils.parseHeaderValue(timestamp));

      if (StringUtils.hasText(response)) {
        restApiException.setMessage(response);
      } else {
        final String message = httpHeaders.getFirst(RestApiExceptionUtils.MESSAGE_HEADER_NAME);
        restApiException.setMessage(
            StringUtils.hasText(message) ? message : RestApiExceptionUtils.NO_MESSAGE_VALUE);
      }

      final String errorCode = httpHeaders.getFirst(RestApiExceptionUtils.CODE_HEADER_NAME);
      if (StringUtils.hasText(errorCode)
          && !RestApiExceptionUtils.NO_ERROR_CODE_VALUE.equals(errorCode)) {
        restApiException.setErrorCode(errorCode);
      }

      final String cls = httpHeaders.getFirst(RestApiExceptionUtils.CLASS_HEADER_NAME);
      if (StringUtils.hasText(cls) && !RestApiExceptionUtils.NO_CLASS_VALUE.equals(cls)) {
        restApiException.setClassName(cls);
      }
    }
    return restApiException;
  }

}