How to handle ClientAbortException in Spring-MVC? - java

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() {
}
}

Related

How to handle exceptions thrown by keycloak in Java?

I am using keycloak authentication with java. The keycloak java adapter throws exceptions in case of invalid tokens etc. Now, I want to catch these exceptions so that I can show custom message and do other stuffs. In the below example I tried to catch exceptions by creating a filter but was unsuccessful.
E.g.:
DEBUG TRACE:
In the following trace, AuthentciatedActionHandler.isAuthorised method throws exception in case of invalid token.
I thought that may be adding a filter with Priority(1) will introduce the filter somewhere in the stack and I can handle the exceptions thrown by keycloak in the filter. But as you can see, filter does not come in stack calls.
(Although in case of valid request, AuthMasterFilter comes in stack trace)
My Jetty config:
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY ); //keycloak
context.setContextPath("/");
context.addFilter(GzipFilter.class, "/*", EnumSet.allOf(DispatcherType.class));
context.addFilter(AuthMasterFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST)); // filter to handle exceptions from keycloak
ResourceConfig resourceConfig = new ResourceConfig(ImmutableSet.<Class<?>>builder().add(KeycloakRestService.class).build());
context.addServlet(new ServletHolder(new ServletContainer(resourceConfig)), "/*");
KeycloakJettyAuthenticator kcAuthenticator = ... // set up keycloak
ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
securityHandler.setAuthenticator(kcAuthenticator);
context.setSecurityHandler(securityHandler);
AUTHMASTERFILTER.java:
#Priority(1)
public class AuthMasterFilter implements Filter{
#Override
public void destroy() { }
#Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
System.out.println("Master called");
}
#Override
public void init(FilterConfig arg0) throws ServletException {}
}

Handling JWT Exception in Spring MVC

I am trying to implement Token Authentication on our REST Api, and currently I am referring to this article. On the article it discusses that on creating the token JWT was used, but my current problem is that every time an invalid token was being passed on to my application an exception is being created which is the JwtException.class and I want to catch that exception using my global exception handler class. I tried also to wrapped the JwtException on my application's exception class but to no avail the exception was not caught.
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler(value={JwtException.class})
public ResponseEntity<?> handleTokenException(JwtException e){
return new ResponseEntity<Object>(HttpStatus.UNAUTHORIZED);
}
#ExceptionHandler(value={InvalidAuthTokenException.class})
public ResponseEntity<?> handleTokenException(InvalidAuthTokenException e){
return new ResponseEntity<Object>(HttpStatus.UNAUTHORIZED);
}
}
Your GlobalExceptionHandler isn't truly global, it will only catch exceptions that occur in your controller (hence ControllerAdvice), the exceptions you are running into are occurring in servlet filters, which is where Spring Security does pretty much all of its work. This little chart may help explain what I am talking about:
PreFilters <- Executed before entering your controller, decryption of JWT is happening here
Controller <- ControllerAdvice will catch all exceptions thrown here
PostFilters <- Executed after exiting your controller
Luckily Spring Security already has mechanisms in place for handling exceptions that occur when doing things like decrypting a JWT in a filter. You will want to update your SpringSecurityConfig like so. Note its important that the ExceptionTranslationFilter is after your StatelessAuthenticationFilter (or whatever you named the filter where the JWT decryption is occurring).
#Configuration
#EnableWebSecurity
#Order(2)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
ExceptionTranslationFilter = new ExceptionTranslationFilter(new AuthenticationExceptionHandler());
http.addFilterAfter(new StatelessAuthenticationFilter(tokenAuthenticationService),
ExceptionTranslationFilter.class);
}
}
public class AuthenticationExceptionHandler implements AuthenticationEntryPoint {
public void commence(HttpServletRequest request, HttpServletResponse, AuthenticationException e) throws IOException, ServletException {
//Logic on how to handle JWT exception goes here
}
}
public class StatelessAuthenticationFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
//DECRYPT YOUR JWT
} catch (Exception e) {
throw new AuthenticationException();//If you get an exception wrap it in a AuthenticationException (or a class that extends it)
}
}
}

Spring MVC Generic Exception Handling

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.

Session Filter redirect trouble

I have problems with AccessFilter in java web. When I am calling /main.jspx it redirect to the login.jsp. But when I am trying to log-in some error appeared
public class AccessFilter implements Filter {
private FilterConfig filterConfig;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpSession session = ((HttpServletRequest) request).getSession();
HttpServletResponse res = (HttpServletResponse) response;
Client client = (Client) session.getAttribute("client");
if (client != null) {
chain.doFilter(request, response);
} else {
RequestDispatcher dispatcher = request.getRequestDispatcher(
ConfigurationManager.getInstance().getProperty(ConfigurationManager.LOGIN_PAGE_PATH));
dispatcher.forward(request, response);
}
}
#Override
public void destroy() {
this.filterConfig = null;
}
}
web.xml:
<filter>
<filter-name>AccessFilter</filter-name>
<filter-class>ua.kpi.shop.filter.AccessFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AccessFilter</filter-name>
<url-pattern>/jsp/main.jspx</url-pattern>
<url-pattern>/jsp/pokemons.jspx</url-pattern>
</filter-mapping>
Error:
HTTP Status 404 - /PokemonsShop/login.jspx
type Status report
message /PokemonsShop/login.jspx
description The requested resource is not available.
filterConfig.getServletContext().getRequestDispatcher takes the absolute path as opposed to request-getRequestDispatcher. Though whether that is the solution I cannot say.
Two things came into my head when seeing your message:
1) Did you check whether the client object is null or not ? May be when executing the login action (method) you are not setting correctly the client into session ?
2) In the server error, it says "not found /PokemonsShop/login.jspx" but in your filter mapping you are mentioning /jsp/xxx. Would it be because your login page is under the folder jsp and you are redirecting (in the filter) to /PokemonsShop/login.jspx, which should be under webapp root folder to be accessible.
Hope one of them be of help

How to handle exceptions thrown while rendering a view in Spring MVC?

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!!

Categories

Resources