I am using the following generic class to handle all types of exceptions in my app. It handles most of the exceptions but fails to for some such as "org.apache.tiles.impl.CannotRenderException". How can I make it catch all types of exceptions?
Some of the technologies that I am using are: Spring 4.0.0.RELEASE, Tiles 2.2, Maven 1.6, Spring Webflow 2.4.0.RELEAS
#ControllerAdvice
class GenericDefaultExceptionHandler {
public static final String DEFAULT_ERROR_VIEW = "error/error";
private static final String DEFAULT_ERROR_SUBJECT = "Exception occurred";
final Logger logger = LoggerFactory.getLogger(getClass());
#Autowired
private MailService mailService;
#ExceptionHandler(value = Exception.class)
public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
throw e;
ModelAndView mav = new ModelAndView();
mav.addObject("exception", e);
mav.addObject("url", req.getRequestURL());
mav.setViewName(DEFAULT_ERROR_VIEW);
//send email to system admin
sendMessageToAdmin(e.toString(),req.getRequestURL().toString());
logger.error(e.toString());
return mav;
}
private void sendMessageToAdmin(String exceptionAsMessage, String url) {
try {
StringBuilder errorMessage = new StringBuilder();
errorMessage.append("Exception on request URL :");
errorMessage.append(url);
errorMessage.append("\n\n");
errorMessage.append("The Exception was: ");
errorMessage.append(exceptionAsMessage);
mailService.sendMailWithSubject(DEFAULT_ERROR_SUBJECT,errorMessage.toString());
} catch (Exception e) {
}
}
}
Thanks
The problem is that your handler catches the exception in your controller, but tiles exception are thrown in the view after the controller has finished its job. You can try to handle them using a filter :
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
...
try {
chain.doFilter(request, response);
}
catch (Exception ex) {
// your stuff
...
throw new ServletException(ex);
}
}
Independantly of that (or in addition), you can also ask the container to use some views when it find exceptions or when the controllers uses sendError with some configuration in the web.xml file :
<!-- error-code related error pages -->
<error-page>
<error-code>404</error-code>
<location>/ErrorHandler</location>
</error-page>
<error-page>
<error-code>403</error-code>
<location>/ErrorHandler</location>
</error-page>
<!-- exception-type related error pages -->
<error-page>
<exception-type>
javax.servlet.ServletException
</exception-type >
<location>/ErrorHandler</location>
</error-page>
<error-page>
<exception-type>java.io.IOException</exception-type >
<location>/ErrorHandler</location>
</error-page>
Yes, the Filter worked for me. I created an exception class that extends GenericFilterBean of Spring filter and registered it inside my ApplicationConfig.
public class ApplicationConfig implements WebApplicationInitializer {
.....
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
.....
FilterRegistration.Dynamic exception = servletContext.addFilter("genericExceptionFilter", new GenericExceptionFilter());
exception.addMappingForUrlPatterns(dispatcherTypes, true, "/*");
.....
}
}
Thank you.
Related
I have simple Spring MVC application where I want to hande 404 Not found exceptions in my Advice Controller class
Configuration:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebConfig.class};
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootConfig.class, SecurityConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addListener(new SessionListener());
FilterRegistration.Dynamic encodingFilter = servletContext.addFilter("encodingFilter", new CharacterEncodingFilter());
encodingFilter.setInitParameter("encoding", "UTF-8");
encodingFilter.setInitParameter("forceEncoding", "true");
encodingFilter.addMappingForUrlPatterns(null, true, "/*");
}
}
Controller:
#Controller
#RequestMapping("/")
public class HomeController {
#RequestMapping(value = "/error/", method = RequestMethod.GET)
public String error(){return "error";}
}
ControllerAdvice:
#ControllerAdvice
public class AdviceController {
#ExceptionHandler(MyOwnException.class)
#ResponseStatus(value= HttpStatus.BAD_REQUEST)
public String checkoutException(CheckoutException e, HttpServletRequest httpServletRequest) {
return "error";
}
}
I can catch my own exceptions when I manually throw MyOwnException but I can't get how to catch NoHandlerFound exception. I need to send 404 error code and appropriate error.jsp page when there is no controller method to handle request
If your webapp is using web.xml it's very simple - just add the following (assuming usage of InternalResourceViewResolver with prefix pointing at your WEB-INF view folder and suffix .jsp). You can have multiple error-page elements of other error codes too.
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
If you are not using web.xml it's more complicated and you'll have to define and register your own ExceptionResolver. Take a look at this spring.io blog article for details on how to do this.
(Edit after comment)
If you want to catch the NoHandlerFound exception you first have to tell Spring to throw it via setting a flag in the DispatcherServlet directly. To do so, in your AppInitializer class add the DispatcherServlet definition on top of what you are currently doing to add the flag:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addListener(new SessionListener());
//BEGIN OF NEW CODE
WebApplicationContext context = getContext();
DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
//we did all this to set the below flag
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet",dispatcherServlet );
//END OF NEW CODE
FilterRegistration.Dynamic encodingFilter = servletContext.addFilter("encodingFilter", new CharacterEncodingFilter());
encodingFilter.setInitParameter("encoding", "UTF-8");
encodingFilter.setInitParameter("forceEncoding", "true");
encodingFilter.addMappingForUrlPatterns(null, true, "/*");
}
Then you can catch the NoHandlerFound exception directly in your AdviceController:
#ControllerAdvice
public class AdviceController {
//..
#ExceptionHandler(NoHandlerFoundException.class)
public String dealWithNoHandlerFoundException(CheckoutException e, HttpServletRequest httpServletRequest) {
return "error";
}
}
I want to handle 404 page not found exception in my Spring MVC web app, I'm using SPRING 4.2.5.RELEASE, I had read several question regarding this topic but the similar questions are using a different spring java configuration.
I have a Global Exception Handler Controller class that have all my Exceptions, this class works fine but I can't handle a 404 page not found exception.
This is the approach that I take following a tutorial
1) I created a class named ResourceNotFoundException that extends from RuntimeException and I putted this annotation over the class definition #ResponseStatus(HttpStatus.NOT_FOUND)
like this:
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
}
2) I created this method in my exception's controller class
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handleResourceNotFoundException() {
return "notFoundJSPPage";
}
But still when I put a URL that doesn't exist I get this error "No mapping found for HTTP request with URI"
The questions that I had read said that I need to enable to true an option for the Dispatcher but since my configuration it's different from the other questions and I don't have a Web.xml I couldn't apply that.
Here it's my Config.java
#EnableWebMvc
#Configuration
#ComponentScan({"config", "controllers"})
public class ConfigMVC extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**").addResourceLocations("/WEB-INF/resources/");
}
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
}
Here is my WebInitializer
public class WebInicializar implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ConfigMVC.class);
ctx.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
Here is my Global Exception Handler Controller
#ControllerAdvice
public class GlobalExceptionHandlerController {
#ExceptionHandler(value = NullPointerException.class)
public String handleNullPointerException(Exception e) {
System.out.println("A null pointer exception ocurred " + e);
return "nullpointerExceptionPage";
}
#ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = Exception.class)
public String handleAllException(Exception e) {
System.out.println("A unknow Exception Ocurred: " + e);
return "unknowExceptionPage";
}
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handleResourceNotFoundException() {
return "notFoundJSPPage";
}
}
And the class I created that extends Runtime Exception
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException{
}
I solved the problem by putting this line in my onStartup method in the WebApplicationInitializer.class
this it's the line I add servlet.setInitParameter("throwExceptionIfNoHandlerFound", "true");
this is how it looks the complete method with the new line I added
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(ConfigMVC.class);
ctx.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servlet.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
Then I created this controller method in my GlobalExceptionHandlerController.class
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handle(NoHandlerFoundException ex) {
return "my404Page";
}
and that solved my problem I deleted the handleResourceNotFoundException controller method in my GlobalExceptionHandlerController.class since it wasn't necessary and also I deleted the exception class ResourceNotFoundException.class that I created
You can also extend AbstractAnnotationConfigDispatcherServletInitializer and override this method:
#Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext) {
final DispatcherServlet dispatcherServlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
OR this one:
#Override
public void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
And finally in your ControlerAdvice use this:
#ExceptionHandler(NoHandlerFoundException.class)
public String error404(Exception ex) {
return new ModelAndView("404");
}
Add following code in any controller and create a 404 page
#GetMapping("/*")
public String handle() {
return "404";
}
I found that the answer by zygimantus didnt work for some reason, so if you also have the same problem , then instead of declaring an "#ExceptionHandler", add one of these to a "#Configuration" class instead. I put mine in my WebMvcConfigurerAdapter
#Bean
public HandlerExceptionResolver handlerExceptionResolver(){
HandlerExceptionResolver myResolver = new HandlerExceptionResolver(){
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) {
//return your 404 page
ModelAndView mav = new ModelAndView("404page");
mav.addObject("error", exception);
return mav;
}
};
return myResolver;
}
But make sure you also follow the rest of zygimantus ie
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
Whenever a client aborts the connection, I'm getting a ClientAbortException logged as follows:
org.apache.catalina.connector.ClientAbortException: java.io.IOException: APR error: -730053
at org.apache.catalina.connector.OutputBuffer.doFlush(OutputBuffer.java:353) ~[catalina.jar:8.0.26]
at org.apache.catalina.connector.OutputBuffer.flush(OutputBuffer.java:317) ~[catalina.jar:8.0.26]
at org.apache.catalina.connector.CoyoteOutputStream.flush(CoyoteOutputStream.java:110) ~[catalina.jar:8.0.26]
at com.fasterxml.jackson.core.json.UTF8JsonGenerator.flush(UTF8JsonGenerator.java:1022) ~[jackson-core-2.6.5.jar:2.6.5]
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:891) ~[jackson-databind-2.6.5.jar:2.6.5]
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:264) ~[spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:100) ~[spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:222) ~[spring-webmvc-4.2.4.RELEASE.jar:4.2.4.RELEASE]
Question: as I don't care if the client aborts the connection, I'd want to prevent logging. Or moreover, prevent my application trying to acutally return a response.
How could I do this?
Could I create some kind of global #ExceptionHandler(ClientAbortException.class) but returning void if I catch any?
Since at least Spring Boot 2.3.4 (and probably before) you can use a #ControllerAdvice annotated class with the following method:
#ExceptionHandler(ClientAbortException.class)
public void handleLockException(ClientAbortException exception, HttpServletRequest request) {
final String message = "ClientAbortException generated by request {} {} from remote address {} with X-FORWARDED-FOR {}";
final String headerXFF = request.getHeader("X-FORWARDED-FOR");
log.warn(message, request.getMethod(), request.getRequestURL(), request.getRemoteAddr(), headerXFF);
}
I've had the same problem and i was unable to do what you tell with Spring MVC and a Exception handler. Some exceptions (Unchecked ones i guess) are not chatched by Spring MVC handlers. What i did was to define a generic filter in web.xml
<!-- Filter for exception handling, for those exceptions don't catched by Spring MVC -->
<filter>
<filter-name>LoggerFilter</filter-name>
<filter-class>com.myproject.commons.filters.ExceptionLoggerServletFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And the source of my filter:
public class ExceptionLoggerServletFilter implements Filter {
private static Log log = LogFactory.getLog(ExceptionLoggerServletFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
try {
chain.doFilter(request, response);
} catch (Throwable e) {
StringBuilder sb = new StringBuilder();
sb.append("Exception detected in ExceptionLoggerServletFilter:");
if (e instanceof org.apache.catalina.connector.ClientAbortException) {
// don't do full log of this error
sb.append(" ClientAbortException");
log.error(sb.toString());
} else {
log.error(sb.toString(), e);
}
throw e;
}
}
#Override
public void destroy() {
}
}
I use #ExceptionHandler to handle exceptions thrown by my web app, in my case my app returns JSON response with HTTP status for error responses to the client.
However, I am trying to figure out how to handle error 404 to return a similar JSON response like with the one handled by #ExceptionHandler
Update:
I mean, when a URL that does not exist is accessed
I use spring 4.0 and java configuration. My working code is:
#ControllerAdvice
public class MyExceptionController {
#ExceptionHandler(NoHandlerFoundException.class)
public ModelAndView handleError404(HttpServletRequest request, Exception e) {
ModelAndView mav = new ModelAndView("/404");
mav.addObject("exception", e);
//mav.addObject("errorcode", "404");
return mav;
}
}
In JSP:
<div class="http-error-container">
<h1>HTTP Status 404 - Page Not Found</h1>
<p class="message-text">The page you requested is not available. You might try returning to the home page.</p>
</div>
For Init param config:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
}
Or via xml:
<servlet>
<servlet-name>rest-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
See Also: Spring MVC Spring Security and Error Handling
With spring > 3.0 use #ResponseStatus
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
...
}
#Controller
public class MyController {
#RequestMapping.....
public void handleCall() {
if (isFound()) {
// do some stuff
}
else {
throw new ResourceNotFoundException();
}
}
}
Simplest way to find out is use the following:
#ExceptionHandler(Throwable.class)
public String handleAnyException(Throwable ex, HttpServletRequest request) {
return ClassUtils.getShortName(ex.getClass());
}
If the URL is within the scope of DispatcherServlet then any 404 caused by mistyping or anything else will be caught by this method but if the URL typed is beyond the URL mapping of the DispatcherServlet then you have to either use:
<error-page>
<exception-type>404</exception-type>
<location>/404error.html</location>
</error-page>
or
Provide "/" mapping to your DispatcherServlet mapping URL so as to handle all the mappings for the particular server instance.
public final class ResourceNotFoundException extends RuntimeException {
}
#ControllerAdvice
public class AppExceptionHandler {
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handleNotFound() {
return "404";
}
}
Just define an Exception, an ExceptionHandler, throw the Exception from your business code controller.
You can use servlet standard way to handle 404 error. Add following code in web.xml
<error-page>
<exception-type>404</exception-type>
<location>/404error.html</location>
</error-page>
I have a Spring MVC application which uses FreeMarker as View technology (But maybe the view technology doesn't really matter for my question). I need to intercept all exceptions which may get thrown during a request.
I have implemented a HandlerExceptionResolver but this resolver is only executed when the exception occurs within a controller. But when a controller returns a ModelAndView and the exception occurs while rendering the view (Because a variable was not found or something like this) then the exception resolver is not called and instead I get a stack trace in the browser window.
I also tried using an exception handler method within the controller which returns the view and annotated it with #ExceptionHandler but this also doesn't work (Most likely again because the exception is not thrown in the controller but in the view).
So is there some Spring mechanism where I can register an exception handler which captures view errors?
A word upfront: if you just need a "static" error page without much logic and model preparation, it should suffice to put a <error-page>-Tag in your web.xml (see below for an example).
Otherwise, there might be better ways to do this, but this works for us:
We use a servlet <filter> in the web.xml that catches all Exceptions and calls our custom ErrorHandler, the same we use inside the Spring HandlerExceptionResolver.
<filter>
<filter-name>errorHandlerFilter</filter-name>
<filter-class>org.example.filter.ErrorHandlerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>errorHandlerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The implementation looks essentially like this:
public class ErrorHandlerFilter implements Filter {
ErrorHandler errorHandler;
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
try {
filterChain.doFilter(request, response);
} catch (Exception ex) {
// call ErrorHandler and dispatch to error jsp
String errorMessage = errorHandler.handle(request, response, ex);
request.setAttribute("errorMessage", errorMessage);
request.getRequestDispatcher("/WEB-INF/jsp/error/dispatch-error.jsp").forward(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
errorHandler = (ErrorHandler) WebApplicationContextUtils
.getRequiredWebApplicationContext(filterConfig.getServletContext())
.getBean("defaultErrorHandler");
}
// ...
}
I believe this should work pretty much the same for FreeMarker templates. Of course if your error view throws an error, you're more or less out of options.
To also catch errors like 404 and prepare the model for it, we use a filter that is mapped to the ERROR dispatcher:
<filter>
<filter-name>errorDispatcherFilter</filter-name>
<filter-class>org.example.filter.ErrorDispatcherFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>errorDispatcherFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/error/dispatch-error.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/WEB-INF/jsp/error/dispatch-error.jsp</location>
</error-page>
The doFilter-Implementation looks like this:
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) servletRequest;
// handle code(s)
final int code = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (code == 404) {
final String uri = (String) request.getAttribute("javax.servlet.error.request_uri");
request.setAttribute("errorMessage", "The requested page '" + uri + "' could not be found.");
}
// notify chain
filterChain.doFilter(servletRequest, servletResponse);
}
You could extends the DispatcherServlet.
In your web.xml replace the generic DispatcherServlet for your own class.
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>com.controller.generic.DispatcherServletHandler</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
Later create your own class DispatcherServletHandler and extends from DispatcherServlet:
public class DispatcherServletHandler extends DispatcherServlet {
private static final String ERROR = "error";
private static final String VIEW_ERROR_PAGE = "/WEB-INF/views/error/view-error.jsp";
#Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
try{
super.doService(request, response);
} catch(Exception ex) {
request.setAttribute(ERROR, ex);
request.getRequestDispatcher(VIEW_ERROR_PAGE).forward(request, response);
}
}
}
And in that page we only have to show a message to the user.
Not sure if my solution works with the problem you're having. Ill just post the way i catch my exceptions to ensure no stack trace is show inside the browser:
I made an AbstractController class with a method that will handle a specific conflict like this:
public class AbstractController {
#ResponseStatus(HttpStatus.CONFLICT)
#ExceptionHandler({OptimisticLockingFailureException.class})
#ResponseBody
public void handleConflict() {
//Do something extra if you want
}
}
This way whenever an exception occurs the user will see a default HTTPResponse status. (eg. 404 Not Found etc..)
I extend this class on all my controller classes to make sure errors are redirected to the AbstractController. This way I don't need to use ExceptionHandler on a specific controller but I can add the globally to all my controllers. (by extending the AbstractController class).
Edit:
After another go on your question, I noticed you're getting errors in your view. Not sure if this way will catch that error..
Hope this helps!!