An exception resulting from an annotation cannot be caught in a regular try / catch block because the result of the annotation is computed before our code is executed.
Example
Take this resource for example:
@Resource
@Path("/example")
public class ExampleResource {
@GET
@Path("/")
@AnnotationThatThrowsException
public Response getExample() {
try {
return Response.status(Response.Status.NO_CONTENT).build();
} catch (Exception e) {
return Response.serverError().entity(e.getMessage()).build();
}
}
}
Here the code that is run by the annotation processor for AnnotationThatThrowsException, throws an exception. This happens before we actually enter the method. Our try / catch block cannot catch the exception. How do we handle this?
Catching exceptions with a filter
In this example we know about an AccessDeniedException that can be thrown from code that is run because of an annotation. We want to return a 403 Forbidden status code when that exception has been thrown.
Depending on your situation, the exception may already have been caught and rethrown. For that reason we also check any throwable to see if it's cause is an AccessDeniedException.
import javax.enterprise.context.ApplicationScoped;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@ApplicationScoped
@WebFilter(filterName = "ExceptionHandlingFilter", urlPatterns = "/*")
public class ExceptionHandlingFilter implements Filter {
private static final Logger LOGGER
= LoggerFactory.getLogger(ExceptionHandlingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException {
try {
chain.doFilter(request, response);
} catch (AccessDeniedException e) {
((HttpServletResponse) response)
.sendError(HttpServletResponse.SC_FORBIDDEN, e.getMessage());
} catch (Throwable t) {
if (t.getCause() != null && t.getCause() instanceof AccessDeniedException) {
((HttpServletResponse) response)
.sendError(HttpServletResponse.SC_FORBIDDEN, t.getCause().getMessage());
} else {
((HttpServletResponse) response)
.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, t.getMessage());
}
}
}
}
The filter should be registered automatically due to the filter's WebFilter annotation. If for some reason that doesn't work one can try registering it like so:
import javax.inject.Inject;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import java.util.EnumSet;
import java.util.Set;
public class FilterInitializer implements ServletContainerInitializer {
@Inject
private ExceptionHandlingFilter exceptionHandlingFilter;
@Override
public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
FilterRegistration.Dynamic reg =
ctx.addFilter("ExceptionHandlingFilter", exceptionHandlingFilter);
reg.setAsyncSupported(true);
reg.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), false, "/*");
}
}
Exception Mappers
Exception mappers are another way to handle exceptions when using JAX-RS. I believe these are the recommended solution for handling previously uncaught exceptions, and it is worth looking in to them. However, I haven't gotten exception mappers to work for an exception from an annotation.