CompositeExceptionResolverFactoryBean.java

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

import com.google.common.collect.ImmutableMap;

import cn.home1.oss.lib.errorhandle.api.ApplicationException;
import cn.home1.oss.lib.errorhandle.api.ConcreteExceptionResolver;
import cn.home1.oss.lib.errorhandle.api.ExceptionResolver;
import cn.home1.oss.lib.errorhandle.api.ExceptionTranslator;
import cn.home1.oss.lib.errorhandle.api.StackTraceIndicator;
import cn.home1.oss.lib.errorhandle.internal.CompositeExceptionResolver;
import cn.home1.oss.lib.errorhandle.internal.resolver.ApplicationErrorExceptionResolver;
import cn.home1.oss.lib.errorhandle.internal.resolver.ConstraintViolationExceptionResolver;
import cn.home1.oss.lib.errorhandle.internal.resolver.DefaultExceptionResolver;
import cn.home1.oss.lib.errorhandle.internal.resolver.HttpMediaTypeNotSupportedExceptionResolver;
import cn.home1.oss.lib.errorhandle.internal.resolver.HttpRequestMethodNotSupportedExceptionResolver;
import cn.home1.oss.lib.errorhandle.internal.resolver.MethodArgumentNotValidExceptionResolver;

import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.ClassUtils;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;

import java.util.Map;

@Setter
@Slf4j
public class CompositeExceptionResolverFactoryBean implements FactoryBean<ExceptionResolver<Throwable>> {

  public static final String CONSTRAINT_VIOLATION_EXCEPTION = "javax.validation.ConstraintViolationException";

  @Autowired
  private ExceptionTranslator exceptionTranslator;
  @Autowired
  private StackTraceIndicator stackTraceIndicator;

  @SuppressWarnings({"rawtypes", "unchecked"})
  static Map<Class<? extends Throwable>, ConcreteExceptionResolver> buildInResolvers( //
    final ConversionService conversionService, //
    final ExceptionTranslator exceptionTranslator, //
    final StackTraceIndicator stackTraceIndicator //
  ) {
    final ImmutableMap.Builder<Class<? extends Throwable>, ConcreteExceptionResolver> builder = ImmutableMap.builder();

    if (ClassUtils.isPresent( //
      CONSTRAINT_VIOLATION_EXCEPTION, //
      Thread.currentThread().getContextClassLoader() //
    )) {
      try {
        final Class clazz = Class.forName(CONSTRAINT_VIOLATION_EXCEPTION);
        builder.put(clazz, new ConstraintViolationExceptionResolver());
      } catch (final ClassNotFoundException ex) {
        // ignore
        if (log.isDebugEnabled()) {
          log.debug("class {} not found", CONSTRAINT_VIOLATION_EXCEPTION, ex);
        }
      }
    }

    builder.put(HttpMediaTypeNotSupportedException.class, new HttpMediaTypeNotSupportedExceptionResolver());
    builder.put(HttpRequestMethodNotSupportedException.class, new HttpRequestMethodNotSupportedExceptionResolver());
    builder.put(MethodArgumentNotValidException.class, new MethodArgumentNotValidExceptionResolver());
    builder.put(ApplicationException.class, new ApplicationErrorExceptionResolver());

    final Map<Class<? extends Throwable>, ConcreteExceptionResolver> map = builder.build();
    map.values().forEach(exceptionResolver -> {
      exceptionResolver.setConversionService(conversionService);
      exceptionResolver.setExceptionTranslator(exceptionTranslator);
      exceptionResolver.setStackTraceIndicator(stackTraceIndicator);
    });

    return map;
  }

  @Override
  public ExceptionResolver<Throwable> getObject() {
    final ConversionService conversionService = new DefaultConversionService(); // TODO conversionService

    @SuppressWarnings("rawtypes")
    final Map<Class<? extends Throwable>, ConcreteExceptionResolver> buildInResolvers = buildInResolvers( //
      conversionService, //
      this.exceptionTranslator, //
      this.stackTraceIndicator //
    );

    final DefaultExceptionResolver defaultResolver = new DefaultExceptionResolver();
    defaultResolver.setConversionService(conversionService);
    defaultResolver.setExceptionTranslator(this.exceptionTranslator);
    defaultResolver.setStackTraceIndicator(this.stackTraceIndicator);

    final CompositeExceptionResolver compositeExceptionResolver = new CompositeExceptionResolver();
    compositeExceptionResolver.setDefaultResolver(defaultResolver);
    compositeExceptionResolver.setResolverMap(buildInResolvers);

    return compositeExceptionResolver;
  }

  @Override
  public Class<?> getObjectType() {
    return ExceptionResolver.class;
  }

  @Override
  public boolean isSingleton() {
    return false;
  }
}