001/*
002 * Copyright 2002-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.web.servlet.mvc.support;
018
019import java.io.IOException;
020import java.util.List;
021
022import javax.servlet.http.HttpServletRequest;
023import javax.servlet.http.HttpServletResponse;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027
028import org.springframework.beans.ConversionNotSupportedException;
029import org.springframework.beans.TypeMismatchException;
030import org.springframework.core.Ordered;
031import org.springframework.http.MediaType;
032import org.springframework.http.converter.HttpMessageNotReadableException;
033import org.springframework.http.converter.HttpMessageNotWritableException;
034import org.springframework.lang.Nullable;
035import org.springframework.util.CollectionUtils;
036import org.springframework.util.StringUtils;
037import org.springframework.validation.BindException;
038import org.springframework.validation.BindingResult;
039import org.springframework.web.HttpMediaTypeNotAcceptableException;
040import org.springframework.web.HttpMediaTypeNotSupportedException;
041import org.springframework.web.HttpRequestMethodNotSupportedException;
042import org.springframework.web.bind.MethodArgumentNotValidException;
043import org.springframework.web.bind.MissingPathVariableException;
044import org.springframework.web.bind.MissingServletRequestParameterException;
045import org.springframework.web.bind.ServletRequestBindingException;
046import org.springframework.web.bind.annotation.ModelAttribute;
047import org.springframework.web.bind.annotation.RequestBody;
048import org.springframework.web.bind.annotation.RequestPart;
049import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
050import org.springframework.web.multipart.MultipartFile;
051import org.springframework.web.multipart.support.MissingServletRequestPartException;
052import org.springframework.web.servlet.ModelAndView;
053import org.springframework.web.servlet.NoHandlerFoundException;
054import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
055
056/**
057 * The default implementation of the {@link org.springframework.web.servlet.HandlerExceptionResolver}
058 * interface, resolving standard Spring MVC exceptions and translating them to corresponding
059 * HTTP status codes.
060 *
061 * <p>This exception resolver is enabled by default in the common Spring
062 * {@link org.springframework.web.servlet.DispatcherServlet}.
063 *
064 * <p>
065 * <table>
066 * <caption>Supported Exceptions</caption>
067 * <thead>
068 * <tr>
069 * <th class="colFirst">Exception</th>
070 * <th class="colLast">HTTP Status Code</th>
071 * </tr>
072 * </thead>
073 * <tbody>
074 * <tr class="altColor">
075 * <td><p>HttpRequestMethodNotSupportedException</p></td>
076 * <td><p>405 (SC_METHOD_NOT_ALLOWED)</p></td>
077 * </tr>
078 * <tr class="rowColor">
079 * <td><p>HttpMediaTypeNotSupportedException</p></td>
080 * <td><p>415 (SC_UNSUPPORTED_MEDIA_TYPE)</p></td>
081 * </tr>
082 * <tr class="altColor">
083 * <td><p>HttpMediaTypeNotAcceptableException</p></td>
084 * <td><p>406 (SC_NOT_ACCEPTABLE)</p></td>
085 * </tr>
086 * <tr class="rowColor">
087 * <td><p>MissingPathVariableException</p></td>
088 * <td><p>500 (SC_INTERNAL_SERVER_ERROR)</p></td>
089 * </tr>
090 * <tr class="altColor">
091 * <td><p>MissingServletRequestParameterException</p></td>
092 * <td><p>400 (SC_BAD_REQUEST)</p></td>
093 * </tr>
094 * <tr class="rowColor">
095 * <td><p>ServletRequestBindingException</p></td>
096 * <td><p>400 (SC_BAD_REQUEST)</p></td>
097 * </tr>
098 * <tr class="altColor">
099 * <td><p>ConversionNotSupportedException</p></td>
100 * <td><p>500 (SC_INTERNAL_SERVER_ERROR)</p></td>
101 * </tr>
102 * <tr class="rowColor">
103 * <td><p>TypeMismatchException</p></td>
104 * <td><p>400 (SC_BAD_REQUEST)</p></td>
105 * </tr>
106 * <tr class="altColor">
107 * <td><p>HttpMessageNotReadableException</p></td>
108 * <td><p>400 (SC_BAD_REQUEST)</p></td>
109 * </tr>
110 * <tr class="rowColor">
111 * <td><p>HttpMessageNotWritableException</p></td>
112 * <td><p>500 (SC_INTERNAL_SERVER_ERROR)</p></td>
113 * </tr>
114 * <tr class="altColor">
115 * <td><p>MethodArgumentNotValidException</p></td>
116 * <td><p>400 (SC_BAD_REQUEST)</p></td>
117 * </tr>
118 * <tr class="rowColor">
119 * <td><p>MissingServletRequestPartException</p></td>
120 * <td><p>400 (SC_BAD_REQUEST)</p></td>
121 * </tr>
122 * <tr class="altColor">
123 * <td><p>BindException</p></td>
124 * <td><p>400 (SC_BAD_REQUEST)</p></td>
125 * </tr>
126 * <tr class="rowColor">
127 * <td><p>NoHandlerFoundException</p></td>
128 * <td><p>404 (SC_NOT_FOUND)</p></td>
129 * </tr>
130 * <tr class="altColor">
131 * <td><p>AsyncRequestTimeoutException</p></td>
132 * <td><p>503 (SC_SERVICE_UNAVAILABLE)</p></td>
133 * </tr>
134 * </tbody>
135 * </table>
136 *
137 * @author Arjen Poutsma
138 * @author Rossen Stoyanchev
139 * @author Juergen Hoeller
140 * @since 3.0
141 * @see org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler
142 */
143public class DefaultHandlerExceptionResolver extends AbstractHandlerExceptionResolver {
144
145        /**
146         * Log category to use when no mapped handler is found for a request.
147         * @see #pageNotFoundLogger
148         */
149        public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
150
151        /**
152         * Additional logger to use when no mapped handler is found for a request.
153         * @see #PAGE_NOT_FOUND_LOG_CATEGORY
154         */
155        protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
156
157
158        /**
159         * Sets the {@linkplain #setOrder(int) order} to {@link #LOWEST_PRECEDENCE}.
160         */
161        public DefaultHandlerExceptionResolver() {
162                setOrder(Ordered.LOWEST_PRECEDENCE);
163                setWarnLogCategory(getClass().getName());
164        }
165
166
167        @Override
168        @Nullable
169        protected ModelAndView doResolveException(
170                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) {
171
172                try {
173                        if (ex instanceof HttpRequestMethodNotSupportedException) {
174                                return handleHttpRequestMethodNotSupported(
175                                                (HttpRequestMethodNotSupportedException) ex, request, response, handler);
176                        }
177                        else if (ex instanceof HttpMediaTypeNotSupportedException) {
178                                return handleHttpMediaTypeNotSupported(
179                                                (HttpMediaTypeNotSupportedException) ex, request, response, handler);
180                        }
181                        else if (ex instanceof HttpMediaTypeNotAcceptableException) {
182                                return handleHttpMediaTypeNotAcceptable(
183                                                (HttpMediaTypeNotAcceptableException) ex, request, response, handler);
184                        }
185                        else if (ex instanceof MissingPathVariableException) {
186                                return handleMissingPathVariable(
187                                                (MissingPathVariableException) ex, request, response, handler);
188                        }
189                        else if (ex instanceof MissingServletRequestParameterException) {
190                                return handleMissingServletRequestParameter(
191                                                (MissingServletRequestParameterException) ex, request, response, handler);
192                        }
193                        else if (ex instanceof ServletRequestBindingException) {
194                                return handleServletRequestBindingException(
195                                                (ServletRequestBindingException) ex, request, response, handler);
196                        }
197                        else if (ex instanceof ConversionNotSupportedException) {
198                                return handleConversionNotSupported(
199                                                (ConversionNotSupportedException) ex, request, response, handler);
200                        }
201                        else if (ex instanceof TypeMismatchException) {
202                                return handleTypeMismatch(
203                                                (TypeMismatchException) ex, request, response, handler);
204                        }
205                        else if (ex instanceof HttpMessageNotReadableException) {
206                                return handleHttpMessageNotReadable(
207                                                (HttpMessageNotReadableException) ex, request, response, handler);
208                        }
209                        else if (ex instanceof HttpMessageNotWritableException) {
210                                return handleHttpMessageNotWritable(
211                                                (HttpMessageNotWritableException) ex, request, response, handler);
212                        }
213                        else if (ex instanceof MethodArgumentNotValidException) {
214                                return handleMethodArgumentNotValidException(
215                                                (MethodArgumentNotValidException) ex, request, response, handler);
216                        }
217                        else if (ex instanceof MissingServletRequestPartException) {
218                                return handleMissingServletRequestPartException(
219                                                (MissingServletRequestPartException) ex, request, response, handler);
220                        }
221                        else if (ex instanceof BindException) {
222                                return handleBindException((BindException) ex, request, response, handler);
223                        }
224                        else if (ex instanceof NoHandlerFoundException) {
225                                return handleNoHandlerFoundException(
226                                                (NoHandlerFoundException) ex, request, response, handler);
227                        }
228                        else if (ex instanceof AsyncRequestTimeoutException) {
229                                return handleAsyncRequestTimeoutException(
230                                                (AsyncRequestTimeoutException) ex, request, response, handler);
231                        }
232                }
233                catch (Exception handlerEx) {
234                        if (logger.isWarnEnabled()) {
235                                logger.warn("Failure while trying to resolve exception [" + ex.getClass().getName() + "]", handlerEx);
236                        }
237                }
238                return null;
239        }
240
241        /**
242         * Handle the case where no request handler method was found for the particular HTTP request method.
243         * <p>The default implementation logs a warning, sends an HTTP 405 error, sets the "Allow" header,
244         * and returns an empty {@code ModelAndView}. Alternatively, a fallback view could be chosen,
245         * or the HttpRequestMethodNotSupportedException could be rethrown as-is.
246         * @param ex the HttpRequestMethodNotSupportedException to be handled
247         * @param request current HTTP request
248         * @param response current HTTP response
249         * @param handler the executed handler, or {@code null} if none chosen
250         * at the time of the exception (for example, if multipart resolution failed)
251         * @return an empty ModelAndView indicating the exception was handled
252         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
253         */
254        protected ModelAndView handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
255                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
256
257                String[] supportedMethods = ex.getSupportedMethods();
258                if (supportedMethods != null) {
259                        response.setHeader("Allow", StringUtils.arrayToDelimitedString(supportedMethods, ", "));
260                }
261                response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, ex.getMessage());
262                return new ModelAndView();
263        }
264
265        /**
266         * Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters}
267         * were found for the PUT or POSTed content.
268         * <p>The default implementation sends an HTTP 415 error, sets the "Accept" header,
269         * and returns an empty {@code ModelAndView}. Alternatively, a fallback view could
270         * be chosen, or the HttpMediaTypeNotSupportedException could be rethrown as-is.
271         * @param ex the HttpMediaTypeNotSupportedException to be handled
272         * @param request current HTTP request
273         * @param response current HTTP response
274         * @param handler the executed handler
275         * @return an empty ModelAndView indicating the exception was handled
276         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
277         */
278        protected ModelAndView handleHttpMediaTypeNotSupported(HttpMediaTypeNotSupportedException ex,
279                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
280
281                response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
282                List<MediaType> mediaTypes = ex.getSupportedMediaTypes();
283                if (!CollectionUtils.isEmpty(mediaTypes)) {
284                        response.setHeader("Accept", MediaType.toString(mediaTypes));
285                }
286                return new ModelAndView();
287        }
288
289        /**
290         * Handle the case where no {@linkplain org.springframework.http.converter.HttpMessageConverter message converters}
291         * were found that were acceptable for the client (expressed via the {@code Accept} header.
292         * <p>The default implementation sends an HTTP 406 error and returns an empty {@code ModelAndView}.
293         * Alternatively, a fallback view could be chosen, or the HttpMediaTypeNotAcceptableException
294         * could be rethrown as-is.
295         * @param ex the HttpMediaTypeNotAcceptableException to be handled
296         * @param request current HTTP request
297         * @param response current HTTP response
298         * @param handler the executed handler
299         * @return an empty ModelAndView indicating the exception was handled
300         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
301         */
302        protected ModelAndView handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
303                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
304
305                response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
306                return new ModelAndView();
307        }
308
309        /**
310         * Handle the case when a declared path variable does not match any extracted URI variable.
311         * <p>The default implementation sends an HTTP 500 error, and returns an empty {@code ModelAndView}.
312         * Alternatively, a fallback view could be chosen, or the MissingPathVariableException
313         * could be rethrown as-is.
314         * @param ex the MissingPathVariableException to be handled
315         * @param request current HTTP request
316         * @param response current HTTP response
317         * @param handler the executed handler
318         * @return an empty ModelAndView indicating the exception was handled
319         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
320         * @since 4.2
321         */
322        protected ModelAndView handleMissingPathVariable(MissingPathVariableException ex,
323                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
324
325                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getMessage());
326                return new ModelAndView();
327        }
328
329        /**
330         * Handle the case when a required parameter is missing.
331         * <p>The default implementation sends an HTTP 400 error, and returns an empty {@code ModelAndView}.
332         * Alternatively, a fallback view could be chosen, or the MissingServletRequestParameterException
333         * could be rethrown as-is.
334         * @param ex the MissingServletRequestParameterException to be handled
335         * @param request current HTTP request
336         * @param response current HTTP response
337         * @param handler the executed handler
338         * @return an empty ModelAndView indicating the exception was handled
339         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
340         */
341        protected ModelAndView handleMissingServletRequestParameter(MissingServletRequestParameterException ex,
342                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
343
344                response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
345                return new ModelAndView();
346        }
347
348        /**
349         * Handle the case when an unrecoverable binding exception occurs - e.g. required header, required cookie.
350         * <p>The default implementation sends an HTTP 400 error, and returns an empty {@code ModelAndView}.
351         * Alternatively, a fallback view could be chosen, or the exception could be rethrown as-is.
352         * @param ex the exception to be handled
353         * @param request current HTTP request
354         * @param response current HTTP response
355         * @param handler the executed handler
356         * @return an empty ModelAndView indicating the exception was handled
357         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
358         */
359        protected ModelAndView handleServletRequestBindingException(ServletRequestBindingException ex,
360                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
361
362                response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
363                return new ModelAndView();
364        }
365
366        /**
367         * Handle the case when a {@link org.springframework.web.bind.WebDataBinder} conversion cannot occur.
368         * <p>The default implementation sends an HTTP 500 error, and returns an empty {@code ModelAndView}.
369         * Alternatively, a fallback view could be chosen, or the ConversionNotSupportedException could be
370         * rethrown as-is.
371         * @param ex the ConversionNotSupportedException to be handled
372         * @param request current HTTP request
373         * @param response current HTTP response
374         * @param handler the executed handler
375         * @return an empty ModelAndView indicating the exception was handled
376         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
377         */
378        protected ModelAndView handleConversionNotSupported(ConversionNotSupportedException ex,
379                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
380
381                sendServerError(ex, request, response);
382                return new ModelAndView();
383        }
384
385        /**
386         * Handle the case when a {@link org.springframework.web.bind.WebDataBinder} conversion error occurs.
387         * <p>The default implementation sends an HTTP 400 error, and returns an empty {@code ModelAndView}.
388         * Alternatively, a fallback view could be chosen, or the TypeMismatchException could be rethrown as-is.
389         * @param ex the TypeMismatchException to be handled
390         * @param request current HTTP request
391         * @param response current HTTP response
392         * @param handler the executed handler
393         * @return an empty ModelAndView indicating the exception was handled
394         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
395         */
396        protected ModelAndView handleTypeMismatch(TypeMismatchException ex,
397                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
398
399                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
400                return new ModelAndView();
401        }
402
403        /**
404         * Handle the case where a {@linkplain org.springframework.http.converter.HttpMessageConverter message converter}
405         * cannot read from an HTTP request.
406         * <p>The default implementation sends an HTTP 400 error, and returns an empty {@code ModelAndView}.
407         * Alternatively, a fallback view could be chosen, or the HttpMessageNotReadableException could be
408         * rethrown as-is.
409         * @param ex the HttpMessageNotReadableException to be handled
410         * @param request current HTTP request
411         * @param response current HTTP response
412         * @param handler the executed handler
413         * @return an empty ModelAndView indicating the exception was handled
414         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
415         */
416        protected ModelAndView handleHttpMessageNotReadable(HttpMessageNotReadableException ex,
417                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
418
419                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
420                return new ModelAndView();
421        }
422
423        /**
424         * Handle the case where a
425         * {@linkplain org.springframework.http.converter.HttpMessageConverter message converter}
426         * cannot write to an HTTP request.
427         * <p>The default implementation sends an HTTP 500 error, and returns an empty {@code ModelAndView}.
428         * Alternatively, a fallback view could be chosen, or the HttpMessageNotWritableException could
429         * be rethrown as-is.
430         * @param ex the HttpMessageNotWritableException to be handled
431         * @param request current HTTP request
432         * @param response current HTTP response
433         * @param handler the executed handler
434         * @return an empty ModelAndView indicating the exception was handled
435         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
436         */
437        protected ModelAndView handleHttpMessageNotWritable(HttpMessageNotWritableException ex,
438                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
439
440                sendServerError(ex, request, response);
441                return new ModelAndView();
442        }
443
444        /**
445         * Handle the case where an argument annotated with {@code @Valid} such as
446         * an {@link RequestBody} or {@link RequestPart} argument fails validation.
447         * <p>By default, an HTTP 400 error is sent back to the client.
448         * @param request current HTTP request
449         * @param response current HTTP response
450         * @param handler the executed handler
451         * @return an empty ModelAndView indicating the exception was handled
452         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
453         */
454        protected ModelAndView handleMethodArgumentNotValidException(MethodArgumentNotValidException ex,
455                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
456
457                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
458                return new ModelAndView();
459        }
460
461        /**
462         * Handle the case where an {@linkplain RequestPart @RequestPart}, a {@link MultipartFile},
463         * or a {@code javax.servlet.http.Part} argument is required but is missing.
464         * <p>By default, an HTTP 400 error is sent back to the client.
465         * @param request current HTTP request
466         * @param response current HTTP response
467         * @param handler the executed handler
468         * @return an empty ModelAndView indicating the exception was handled
469         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
470         */
471        protected ModelAndView handleMissingServletRequestPartException(MissingServletRequestPartException ex,
472                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
473
474                response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
475                return new ModelAndView();
476        }
477
478        /**
479         * Handle the case where an {@linkplain ModelAttribute @ModelAttribute} method
480         * argument has binding or validation errors and is not followed by another
481         * method argument of type {@link BindingResult}.
482         * <p>By default, an HTTP 400 error is sent back to the client.
483         * @param request current HTTP request
484         * @param response current HTTP response
485         * @param handler the executed handler
486         * @return an empty ModelAndView indicating the exception was handled
487         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
488         */
489        protected ModelAndView handleBindException(BindException ex, HttpServletRequest request,
490                        HttpServletResponse response, @Nullable Object handler) throws IOException {
491
492                response.sendError(HttpServletResponse.SC_BAD_REQUEST);
493                return new ModelAndView();
494        }
495
496        /**
497         * Handle the case where no handler was found during the dispatch.
498         * <p>The default implementation sends an HTTP 404 error and returns an empty
499         * {@code ModelAndView}. Alternatively, a fallback view could be chosen,
500         * or the NoHandlerFoundException could be rethrown as-is.
501         * @param ex the NoHandlerFoundException to be handled
502         * @param request current HTTP request
503         * @param response current HTTP response
504         * @param handler the executed handler, or {@code null} if none chosen
505         * at the time of the exception (for example, if multipart resolution failed)
506         * @return an empty ModelAndView indicating the exception was handled
507         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
508         * @since 4.0
509         */
510        protected ModelAndView handleNoHandlerFoundException(NoHandlerFoundException ex,
511                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
512
513                pageNotFoundLogger.warn(ex.getMessage());
514                response.sendError(HttpServletResponse.SC_NOT_FOUND);
515                return new ModelAndView();
516        }
517
518        /**
519         * Handle the case where an async request timed out.
520         * <p>The default implementation sends an HTTP 503 error.
521         * @param ex the {@link AsyncRequestTimeoutException }to be handled
522         * @param request current HTTP request
523         * @param response current HTTP response
524         * @param handler the executed handler, or {@code null} if none chosen
525         * at the time of the exception (for example, if multipart resolution failed)
526         * @return an empty ModelAndView indicating the exception was handled
527         * @throws IOException potentially thrown from {@link HttpServletResponse#sendError}
528         * @since 4.2.8
529         */
530        protected ModelAndView handleAsyncRequestTimeoutException(AsyncRequestTimeoutException ex,
531                        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
532
533                if (!response.isCommitted()) {
534                        response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
535                }
536                else {
537                        logger.warn("Async request timed out");
538                }
539                return new ModelAndView();
540        }
541
542        /**
543         * Invoked to send a server error. Sets the status to 500 and also sets the
544         * request attribute "javax.servlet.error.exception" to the Exception.
545         */
546        protected void sendServerError(Exception ex, HttpServletRequest request, HttpServletResponse response)
547                        throws IOException {
548
549                request.setAttribute("javax.servlet.error.exception", ex);
550                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
551        }
552
553}