1 package de.juplo.thymeproxy;
4 import java.io.IOException;
5 import java.util.ArrayList;
6 import java.util.Collections;
7 import java.util.LinkedList;
10 import java.util.Properties;
11 import javax.servlet.ServletException;
12 import javax.servlet.http.HttpServletRequest;
13 import javax.servlet.http.HttpServletResponse;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16 import org.springframework.beans.factory.BeanFactoryUtils;
17 import org.springframework.beans.factory.BeanInitializationException;
18 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
19 import org.springframework.boot.autoconfigure.web.BasicErrorController;
20 import org.springframework.boot.autoconfigure.web.ErrorAttributes;
21 import org.springframework.boot.autoconfigure.web.ErrorProperties;
22 import org.springframework.context.ApplicationContext;
23 import org.springframework.core.annotation.AnnotationAwareOrderComparator;
24 import org.springframework.core.io.ClassPathResource;
25 import org.springframework.core.io.support.PropertiesLoaderUtils;
26 import org.springframework.http.MediaType;
27 import org.springframework.util.ClassUtils;
28 import org.springframework.util.StringUtils;
29 import org.springframework.web.servlet.DispatcherServlet;
30 import static org.springframework.web.servlet.DispatcherServlet.HANDLER_EXCEPTION_RESOLVER_BEAN_NAME;
31 import org.springframework.web.servlet.HandlerExceptionResolver;
32 import org.springframework.web.servlet.ModelAndView;
40 public class ExceptionResolverErrorController extends BasicErrorController
42 private final static Logger LOG =
43 LoggerFactory.getLogger(ExceptionResolverErrorController.class);
45 public final static String EXCEPTION_ATTRIBUTE =
46 "javax.servlet.error.exception";
50 * Name of the class path resource (relative to the DispatcherServlet class)
51 * that defines DispatcherServlet's default strategy names.
53 private static final String DEFAULT_STRATEGIES_PATH =
54 "DispatcherServlet.properties";
55 private static final Properties DEFAULT_STRATEGIES;
59 // Load default strategy implementations from properties file.
60 // This is currently strictly internal and not meant to be customized
61 // by application developers.
64 ClassPathResource resource =
65 new ClassPathResource(
66 DEFAULT_STRATEGIES_PATH,
67 DispatcherServlet.class
69 DEFAULT_STRATEGIES = PropertiesLoaderUtils.loadProperties(resource);
71 catch (IOException ex)
73 throw new IllegalStateException(
74 "Could not load 'DispatcherServlet.properties': " +
81 /** List of HandlerExceptionResolvers used by this servlet */
82 private List<HandlerExceptionResolver> handlerExceptionResolvers;
84 /** Detect all HandlerExceptionResolvers or just expect "handlerExceptionResolver" bean? */
85 private boolean detectAllHandlerExceptionResolvers = true;
88 ExceptionResolverErrorController(
89 ApplicationContext context,
90 ErrorAttributes errorAttributes,
91 ErrorProperties errorProperties
94 super(errorAttributes, errorProperties);
95 initHandlerExceptionResolvers(context);
100 public ModelAndView errorHtml(
101 HttpServletRequest request,
102 HttpServletResponse response
105 Map<String, Object> model =
108 isIncludeStackTrace(request, MediaType.TEXT_HTML)
111 ModelAndView view = null;
113 Exception e = (Exception)request.getAttribute(EXCEPTION_ATTRIBUTE);
116 if (e instanceof ServletException )
118 ServletException n = (ServletException)e;
119 Throwable t = n.getRootCause();
120 if (t != null && t instanceof Exception)
124 for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers)
126 view = resolver.resolveException(request, response, null, e);
133 view.addAllObjects(model);
138 return new ModelAndView("error", model);
143 * Initialize the HandlerExceptionResolver used by this class.
145 * If no bean is defined with the given name in the BeanFactory for this
147 * we default to no exception resolver.
149 private void initHandlerExceptionResolvers(ApplicationContext context)
151 this.handlerExceptionResolvers = null;
153 if (this.detectAllHandlerExceptionResolvers)
155 // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
156 Map<String, HandlerExceptionResolver> matchingBeans =
157 BeanFactoryUtils.beansOfTypeIncludingAncestors(
159 HandlerExceptionResolver.class,
163 if (!matchingBeans.isEmpty())
165 this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
166 // We keep HandlerExceptionResolvers in sorted order.
167 AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
174 HandlerExceptionResolver her =
176 HANDLER_EXCEPTION_RESOLVER_BEAN_NAME,
177 HandlerExceptionResolver.class
179 this.handlerExceptionResolvers = Collections.singletonList(her);
181 catch (NoSuchBeanDefinitionException e)
183 // Ignore, no HandlerExceptionResolver is fine too.
187 // Ensure we have at least some HandlerExceptionResolvers, by registering
188 // default HandlerExceptionResolvers if no other resolvers are found.
189 if (this.handlerExceptionResolvers == null)
191 this.handlerExceptionResolvers =
192 getDefaultStrategies(context, HandlerExceptionResolver.class);
193 LOG.debug("No HandlerExceptionResolvers found: using default");
198 * Create a List of default strategy objects for the given strategy interface.
200 * The default implementation uses the "DispatcherServlet.properties" file (in
202 * package as the DispatcherServlet class) to determine the class names. It
204 * the strategy objects through the context's BeanFactory.
206 * @param context the current WebApplicationContext
207 * @param strategyInterface the strategy interface
208 * @return the List of corresponding strategy objects
210 @SuppressWarnings("unchecked")
211 protected <T> List<T> getDefaultStrategies(
212 ApplicationContext context,
213 Class<T> strategyInterface
216 String key = strategyInterface.getName();
217 String value = DEFAULT_STRATEGIES.getProperty(key);
220 String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
221 List<T> strategies = new ArrayList<>(classNames.length);
222 for (String className : classNames)
229 DispatcherServlet.class.getClassLoader()
231 Object strategy = createDefaultStrategy(context, clazz);
232 strategies.add((T) strategy);
234 catch (ClassNotFoundException ex)
236 throw new BeanInitializationException(
237 "Could not find DispatcherServlet's default strategy class [" +
238 className + "] for interface [" + key + "]",
242 catch (LinkageError err)
244 throw new BeanInitializationException(
245 "Error loading DispatcherServlet's default strategy class [" +
246 className + "] for interface [" + key +
247 "]: problem with class file or dependent class",
256 return new LinkedList<>();
261 * Create a default strategy.
263 * The default implementation uses
264 * {@link org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean}.
266 * @param context the current WebApplicationContext
267 * @param clazz the strategy implementation class to instantiate
268 * @return the fully configured strategy instance
270 * org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
272 * org.springframework.beans.factory.config.AutowireCapableBeanFactory#createBean
274 protected Object createDefaultStrategy(
275 ApplicationContext context,
279 return context.getAutowireCapableBeanFactory().createBean(clazz);
284 * Set whether to detect all HandlerExceptionResolver beans in this servlet's
285 * context. Otherwise,
286 * just a single bean with name "handlerExceptionResolver" will be expected.
288 * Default is "true". Turn this off if you want this servlet to use a single
289 * HandlerExceptionResolver, despite multiple HandlerExceptionResolver beans
290 * being defined in the context.
292 public void setDetectAllHandlerExceptionResolvers(boolean resolvers)
294 detectAllHandlerExceptionResolvers = resolvers;