-package de.juplo.thymeproxy;
-
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Properties;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.BeanFactoryUtils;
-import org.springframework.beans.factory.BeanInitializationException;
-import org.springframework.beans.factory.NoSuchBeanDefinitionException;
-import org.springframework.boot.autoconfigure.web.BasicErrorController;
-import org.springframework.boot.autoconfigure.web.ErrorAttributes;
-import org.springframework.boot.autoconfigure.web.ErrorProperties;
-import org.springframework.context.ApplicationContext;
-import org.springframework.core.annotation.AnnotationAwareOrderComparator;
-import org.springframework.core.io.ClassPathResource;
-import org.springframework.core.io.support.PropertiesLoaderUtils;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.StringUtils;
-import org.springframework.web.servlet.DispatcherServlet;
-import static org.springframework.web.servlet.DispatcherServlet.HANDLER_EXCEPTION_RESOLVER_BEAN_NAME;
-import org.springframework.web.servlet.HandlerExceptionResolver;
-import org.springframework.web.servlet.ModelAndView;
-
-
-
-/**
- *
- * @author kai
- */
-public class ExceptionResolverErrorController extends BasicErrorController
-{
- private final static Logger LOG =
- LoggerFactory.getLogger(ExceptionResolverErrorController.class);
-
- public final static String EXCEPTION_ATTRIBUTE =
- "javax.servlet.error.exception";
-
-
- /**
- * Name of the class path resource (relative to the DispatcherServlet class)
- * that defines DispatcherServlet's default strategy names.
- */
- private static final String DEFAULT_STRATEGIES_PATH =
- "DispatcherServlet.properties";
-
- private static final Properties DEFAULT_STRATEGIES;
- static
- {
- // Load default strategy implementations from properties file.
- // This is currently strictly internal and not meant to be customized
- // by application developers.
- try
- {
- ClassPathResource resource =
- new ClassPathResource(
- DEFAULT_STRATEGIES_PATH,
- DispatcherServlet.class
- );
- DEFAULT_STRATEGIES = PropertiesLoaderUtils.loadProperties(resource);
- }
- catch (IOException ex)
- {
- throw new IllegalStateException(
- "Could not load 'DispatcherServlet.properties': " +
- ex.getMessage()
- );
- }
- }
-
-
- /** Name of the default-error-view */
- private String defaultErrorView = "error";
-
- /** Mapping from HTTP-status-codes to specialized error-pages */
- private Map<HttpStatus, String> errorMappings = new HashMap<>();
-
- /** List of HandlerExceptionResolvers used by this servlet */
- private List<HandlerExceptionResolver> handlerExceptionResolvers;
-
- /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
- private boolean detectAllHandlerExceptionResolvers = true;
-
-
- ExceptionResolverErrorController(
- ApplicationContext context,
- ErrorAttributes errorAttributes,
- ErrorProperties errorProperties
- )
- {
- super(errorAttributes, errorProperties);
- initHandlerExceptionResolvers(context);
- }
-
-
- @Override
- public ModelAndView errorHtml(
- HttpServletRequest request,
- HttpServletResponse response
- )
- {
- Map<String, Object> model =
- getErrorAttributes(
- request,
- isIncludeStackTrace(request, MediaType.TEXT_HTML)
- );
-
- ModelAndView view = null;
-
- Exception e = (Exception)request.getAttribute(EXCEPTION_ATTRIBUTE);
- if (e != null)
- {
- if (e instanceof ServletException )
- {
- ServletException n = (ServletException)e;
- Throwable t = n.getRootCause();
- if (t != null && t instanceof Exception)
- e = (Exception)t;
- }
-
- for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers)
- {
- view = resolver.resolveException(request, response, null, e);
- if (view != null)
- break;
- }
-
- if (view != null)
- {
- view.addAllObjects(model);
- return view;
- }
- }
-
- String viewName = null;
- Integer code = (Integer)model.get("status");
- try
- {
- HttpStatus status = HttpStatus.valueOf(code);
- viewName = errorMappings.get(status);
- }
- catch(Throwable t)
- {
- LOG.warn("cannot map status-code {}: {}", code, t.getMessage());
- }
- if (viewName == null)
- viewName = defaultErrorView;
- return new ModelAndView(viewName, model);
- }
-
-
- /**
- * Initialize the HandlerExceptionResolver used by this class.
- * <p>
- * If no bean is defined with the given name in the BeanFactory for this
- * namespace,
- * we default to no exception resolver.
- */
- private void initHandlerExceptionResolvers(ApplicationContext context)
- {
- this.handlerExceptionResolvers = null;
-
- if (this.detectAllHandlerExceptionResolvers)
- {
- // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
- Map<String, HandlerExceptionResolver> matchingBeans =
- BeanFactoryUtils.beansOfTypeIncludingAncestors(
- context,
- HandlerExceptionResolver.class,
- true,
- false
- );
- if (!matchingBeans.isEmpty())
- {
- this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
- // We keep HandlerExceptionResolvers in sorted order.
- AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
- }
- }
- else
- {
- try
- {
- HandlerExceptionResolver her =
- context.getBean(
- HANDLER_EXCEPTION_RESOLVER_BEAN_NAME,
- HandlerExceptionResolver.class
- );
- this.handlerExceptionResolvers = Collections.singletonList(her);
- }
- catch (NoSuchBeanDefinitionException e)
- {
- // Ignore, no HandlerExceptionResolver is fine too.
- }
- }
-
- // Ensure we have at least some HandlerExceptionResolvers, by registering
- // default HandlerExceptionResolvers if no other resolvers are found.
- if (this.handlerExceptionResolvers == null)
- {
- this.handlerExceptionResolvers =
- getDefaultStrategies(context, HandlerExceptionResolver.class);
- LOG.debug("No HandlerExceptionResolvers found: using default");
- }
- }
-
- /**
- * Create a List of default strategy objects for the given strategy interface.
- * <p>
- * The default implementation uses the "DispatcherServlet.properties" file (in
- * the same
- * package as the DispatcherServlet class) to determine the class names. It
- * instantiates
- * the strategy objects through the context's BeanFactory.
- *
- * @param context the current WebApplicationContext
- * @param strategyInterface the strategy interface
- * @return the List of corresponding strategy objects
- */
- @SuppressWarnings("unchecked")
- protected <T> List<T> getDefaultStrategies(
- ApplicationContext context,
- Class<T> strategyInterface
- )
- {
- String key = strategyInterface.getName();
- String value = DEFAULT_STRATEGIES.getProperty(key);
- if (value != null)
- {
- String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
- List<T> strategies = new ArrayList<>(classNames.length);
- for (String className : classNames)
- {
- try
- {
- Class<?> clazz =
- ClassUtils.forName(
- className,
- DispatcherServlet.class.getClassLoader()
- );
- Object strategy = createDefaultStrategy(context, clazz);
- strategies.add((T) strategy);
- }
- catch (ClassNotFoundException ex)
- {
- throw new BeanInitializationException(
- "Could not find DispatcherServlet's default strategy class [" +
- className + "] for interface [" + key + "]",
- ex
- );
- }
- catch (LinkageError err)
- {
- throw new BeanInitializationException(
- "Error loading DispatcherServlet's default strategy class [" +
- className + "] for interface [" + key +
- "]: problem with class file or dependent class",
- err
- );
- }
- }
- return strategies;
- }
- else
- {
- return new LinkedList<>();
- }
- }
-
- /**
- * Create a default strategy.
- * <p>
- * The default implementation uses
- * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}.
- *
- * @param context the current WebApplicationContext
- * @param clazz the strategy implementation class to instantiate
- * @return the fully configured strategy instance
- * @see
- * org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
- * @see
- * org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean
- */
- protected Object createDefaultStrategy(
- ApplicationContext context,
- Class<?> clazz
- )
- {
- return context.getAutowireCapableBeanFactory().createBean(clazz);
- }
-
-
- /**
- * @
- * @see #addErrorMapping(HttpStatus, String)
- */
- public String addErrorMapping(Integer status, String viewName)
- {
- if (status == null)
- throw new IllegalArgumentException("The status must not be null");
- return addErrorMapping(HttpStatus.valueOf(status), viewName);
- }
-
- /**
- * Adds a mapping from a {@link HttpStatus} to a view.
- *
- * @param status The {@link HttpStatus}, that should be mapped.
- * @param viewName The name of the view, the status should be mapped to.
- * @return The name of the view, the status was previously mapped to, or
- * <code>null</code>, if the status was not mapped before.
- */
- public String addErrorMapping(HttpStatus status, String viewName)
- {
- if (!StringUtils.hasText(viewName))
- throw new IllegalArgumentException("The view-name must not be empty!");
- if (status == null)
- throw new IllegalArgumentException("The status must not be null!");
- return errorMappings.put(status, viewName);
- }
-
- /**
- * Sets mappings from {@link HttpStatus} to specialized error-views.
- * @param mappings The mappings to set.
- */
- public void setErrorMappings(Map<HttpStatus, String> mappings)
- {
- errorMappings = mappings;
- }
-
- /**
- * Sets the default error-view for not-mapped status-codes.
- * @param view The default error-view to set.
- */
- public void setDefaultErrorView(String view)
- {
- defaultErrorView = view;
- }
-
- /**
- * Set whether to detect all HandlerExceptionResolver beans in this servlet's
- * context. Otherwise,
- * just a single bean with name "handlerExceptionResolver" will be expected.
- * <p>
- * Default is "true". Turn this off if you want this servlet to use a single
- * HandlerExceptionResolver, despite multiple HandlerExceptionResolver beans
- * being defined in the context.
- */
- public void setDetectAllHandlerExceptionResolvers(boolean resolvers)
- {
- detectAllHandlerExceptionResolvers = resolvers;
- }
-}