Sobes.tech
Middle

How to handle exceptions thrown by another servlet in an application?

sobes.tech AI

Answer from AI

In the standard servlet architecture, exceptions thrown by one servlet cannot be directly handled by another servlet within the current request. Each request is processed by a separate thread associated with the lifecycle of a single servlet.

However, similar behavior can be achieved using the following approaches:

  1. Using the servlet container's error handling mechanism:

    • In web.xml, you can configure error pages for specific exception types or HTTP status codes. The servlet container (e.g., Tomcat, Jetty) will redirect to this page when an exception occurs.
    • On the error page (which can be another servlet or JSP), you can retrieve exception information from request attributes.
    • Attributes are accessible via request.getAttribute(). Keys include javax.servlet.error.exception, javax.servlet.error.exception_type, javax.servlet.error.message, javax.servlet.error.request_uri.
    <error-page>
        <exception-type>java.lang.RuntimeException</exception-type>
        <location>/errorServlet</location>
    </error-page>
    <error-page>
        <error-code>500</error-code>
        <location>/errorServlet</location>
    </error-page>
    
    // ErrorServlet.java
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
    
    @WebServlet("/errorServlet")
    public class ErrorServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            Throwable throwable = (Throwable) request.getAttribute("javax.servlet.error.exception");
            Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
            String servletName = (String) request.getAttribute("javax.servlet.error.servlet_name");
            String requestUri = (String) request.getAttribute("javax.servlet.error.request_uri");
    
            response.setContentType("text/html");
            PrintWriter out = response.getWriter();
            out.println("<html><body>");
            out.println("<h2>Error!</h2>");
            out.println("<p>Status code: " + statusCode + "</p>");
            out.println("<p>Servlet: " + (servletName != null ? servletName : "Unknown") + "</p>");
            out.println("<p>Request URI: " + (requestUri != null ? requestUri : "Unknown") + "</p>");
            if (throwable != null) {
                out.println("<p>Exception type: " + throwable.getClass().getName() + "</p>");
                out.println("<p>Message: " + throwable.getMessage() + "</p>");
                // Stack trace can also be printed for debugging
                // throwable.printStackTrace(out);
            }
            out.println("</body></html>");
        }
    }
    
  2. Using servlet filters:

    • A filter can wrap the doFilter chain in a try-catch block.
    • If a servlet in the chain throws an exception, the filter catches it.
    • Inside the catch block, the filter can either handle the exception itself or forward the request to a dedicated error handler (possibly another servlet) using request.getRequestDispatcher("/errorServlet").forward(request, response).
    // ExceptionHandlingFilter.java
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @WebFilter("/*") // Applies to all requests
    public class ExceptionHandlingFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            // Filter initialization
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest httpRequest = (HttpServletRequest) request;
            HttpServletResponse httpResponse = (HttpServletResponse) response;
    
            try {
                chain.doFilter(request, response); // Pass request further down the chain (to servlet or next filter)
            } catch (Exception e) {
                // Catch exception thrown by servlet or next filter
                System.err.println("Error occurred during request processing for URI: " + httpRequest.getRequestURI());
                e.printStackTrace(); // Log the error
    
                // Save error info for handler
                httpRequest.setAttribute("javax.servlet.error.exception", e);
                httpRequest.setAttribute("javax.servlet.error.message", e.getMessage());
                httpRequest.setAttribute("javax.servlet.error.request_uri", httpRequest.getRequestURI());
                // Optionally set status code
                httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 500 Internal Server Error
    
                // Forward to error handling servlet
                httpRequest.getRequestDispatcher("/errorServlet").forward(httpRequest, httpResponse);
            }
        }
    
        @Override
        public void destroy() {
            // Cleanup resources
        }
    }
    

These two approaches allow centralized error handling and prevent default container error messages from being shown to the user. The first method (via web.xml) is simpler for basic status code and exception type handling. The second method (via filter) offers more flexibility, allowing additional logic (e.g., logging) before forwarding or handling.

How to handle exceptions thrown by another servlet in… - sobes.tech