ConcreteExceptionResolver.java

package cn.home1.oss.lib.errorhandle.api;

import static org.springframework.util.StringUtils.isEmpty;

import com.google.common.collect.ImmutableMap;

import cn.home1.oss.lib.errorhandle.api.ExceptionTranslator.Location;

import org.springframework.core.convert.ConversionService;
import org.springframework.http.HttpHeaders;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.context.request.RequestAttributes;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import javax.validation.ConstraintViolationException;

/**
 * Created by zhanghaolun on 16/8/11.
 */
public interface ConcreteExceptionResolver<T extends Throwable> extends ExceptionResolver<T> {

  Class<T> getExceptionClass();

  /**
   * set conversionService.
   *
   * @param conversionService convert value in validation error into string
   */
  void setConversionService(ConversionService conversionService);

  ExceptionTranslator getExceptionTranslator();

  void setExceptionTranslator(ExceptionTranslator exceptionTranslator);

  void setStackTraceIndicator(StackTraceIndicator stackTraceIndicator);

  default Optional<HttpHeaders> createHeaders(final RequestAttributes request, final T throwable) {
    return Optional.of(ResolvedError.newHttpHeaders());
  }

  /**
   * find a request attribute.
   *
   * @param requestAttributes requestAttributes
   * @param name              name
   * @param <A>               attributeType
   * @return request attribute
   */
  @SuppressWarnings("unchecked")
  default <A> A getAttribute(final RequestAttributes requestAttributes, final String name) {
    return (A) requestAttributes.getAttribute(name, RequestAttributes.SCOPE_REQUEST);
  }

  default <E extends Throwable> String message( //
    final RequestAttributes requestAttributes, //
    final E throwable, //
    final Optional<List<ValidationError>> validationErrorsOptional //
  ) {
    final Object fromAttribute = getAttribute(requestAttributes, "javax.servlet.error.message");
    final String fromError;
    if (throwable != null) {
      if (!validationErrorsOptional.isPresent()) {
        fromError = throwable.getMessage();
      } else {
        final int errorCount = validationErrorsOptional.get().size();
        fromError = errorCount > 0 ? //
          "Validation failed. Validation errors count: " + errorCount : //
          "No validation errors";
      }
    } else {
      fromError = null;
    }
    final String result;
    if ((!isEmpty(fromAttribute) || fromError == null) && !(throwable instanceof BindingResult)) {
      result = isEmpty(fromAttribute) ? "No message available" : fromAttribute.toString();
    } else {
      result = fromError;
    }
    return result;
  }

  default Optional<Location> find(final T throwable) {
    final ExceptionTranslator exceptionTranslator = this.getExceptionTranslator();
    return exceptionTranslator != null ? exceptionTranslator.find(throwable) : Optional.empty();
  }

  default Optional<Integer> status( //
    final RequestAttributes requestAttributes, //
    final Location location, //
    final T throwable //
  ) {
    final ExceptionTranslator exceptionTranslator = this.getExceptionTranslator();
    final Optional<Integer> translated = exceptionTranslator != null ? //
      exceptionTranslator.status(location) : //
      Optional.empty();
    final Integer fromAttribute = this.getAttribute(requestAttributes, "javax.servlet.error.status_code");
    return translated.isPresent() ? translated : Optional.ofNullable(fromAttribute);
  }

  /**
   * Translate exception and generates localizedMessage.
   *
   * @param request   The current request.
   * @param location  The location get data from.
   * @param throwable throwable
   * @return A TranslateResult.
   */
  default Optional<String> localizedMessage( //
    final RequestAttributes request, //
    final Location location, //
    final T throwable //
  ) {
    final ExceptionTranslator exceptionTranslator = this.getExceptionTranslator();
    final Optional<String> result;
    if (exceptionTranslator != null) {
      final Map<String, Serializable> contextVariables = ImmutableMap.of();
      final String template = exceptionTranslator.template(location).orElse(null);
      result = exceptionTranslator.localizedMessage(template, request, throwable, contextVariables);
    } else {
      result = Optional.empty();
    }
    return result;
  }

  /**
   * validation errors.
   *
   * @param throwable {@link ConstraintViolationException}, {@link MethodArgumentNotValidException}
   * @return validation errors
   */
  default Optional<List<ValidationError>> validationErrors(final T throwable) {
    return Optional.empty();
  }
}