Sobes.tech
Middle

Ako spracovať v aplikácii výnimky, ktoré boli vyhodené iným servletem?

sobes.tech AI

Odpoveď od AI

V štandardnej servletovej architektúre výnimky, ktoré vyhodí jeden servlet, nemôžu byť priamo spracované iným servletom v rámci aktuálnej požiadavky. Každá požiadavka sa spracováva samostatným vláknom, ktoré je spojené s životným cyklom jedného servleta.

Avšak, je možné dosiahnuť podobné správanie pomocou nasledujúcich prístupov:

  1. Použitie mechanizmu spracovania chýb kontajnera servletov:

    • V web.xml je možné nakonfigurovať stránky chýb pre určité typy výnimiek alebo kódy stavu HTTP. Kontajner servletov (napríklad Tomcat, Jetty) presmeruje na túto stránku pri výskyte výnimky.
    • Na stránke chyby (ktorá môže byť iným servletom alebo JSP) je možné získať informácie o výnimke z atribútov požiadavky.
    • Atribúty sú dostupné cez request.getAttribute(). Kľúče zahŕňajú 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>Chyba!</h2>");
            out.println("<p>Kód stavu: " + statusCode + "</p>");
            out.println("<p>Servlet: " + (servletName != null ? servletName : "Neznáme") + "</p>");
            out.println("<p>URI požiadavky: " + (requestUri != null ? requestUri : "Neznáme") + "</p>");
            if (throwable != null) {
                out.println("<p>Typ výnimky: " + throwable.getClass().getName() + "</p>");
                out.println("<p>Správa: " + throwable.getMessage() + "</p>");
                // Výpis stack trace pre ladenie
                // throwable.printStackTrace(out);
            }
            out.println("</body></html>");
        }
    }
    
  2. Použitie servlet filterov:

    • Filter môže obaliť reťazec doFilter do bloku try-catch.
    • Ak servlet v reťazci vyhodí výnimku, filter ju zachytí.
    • Vo vnútri bloku catch môže filter buď sám spracovať výnimku, alebo presmerovať požiadavku na špeciálny spracovateľ chýb (možno iný servlet) pomocou 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("/*") // Filter sa aplikuje na všetky požiadavky
    public class ExceptionHandlingFilter implements Filter {
    
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            // Inicializácia filtra
        }
    
        @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); // Odovzdanie požiadavky ďalej v reťazci (k servlete alebo ďalším filtrom)
            } catch (Exception e) {
                // Zachytenie výnimky, ktorú vyhodil servlete alebo ďalší filter
                System.err.println("Vyskytla sa chyba pri spracovaní požiadavky pre URI: " + httpRequest.getRequestURI());
                e.printStackTrace(); // Logovanie chyby
    
                // Uloženie informácií o chybe pre spracovateľa
                httpRequest.setAttribute("javax.servlet.error.exception", e);
                httpRequest.setAttribute("javax.servlet.error.message", e.getMessage());
                httpRequest.setAttribute("javax.servlet.error.request_uri", httpRequest.getRequestURI());
                // Môže sa nastaviť kód stavu, ak je známy
                httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // 500 Interná chyba servera
    
                // Presmerovanie na servlet spracovania chýb
                httpRequest.getRequestDispatcher("/errorServlet").forward(httpRequest, httpResponse);
            }
        }
    
        @Override
        public void destroy() {
            // Uvoľnenie zdrojov filtra
        }
    }
    

Tieto dva prístupy umožňujú centralizovanú správu chýb a zabraňujú zobrazovaniu štandardných správ o chybe kontajnera používateľovi. Prvý spôsob (cez web.xml) je jednoduchší pre základnú správu kódov stavu a typov výnimiek. Druhý spôsob (cez filter) poskytuje väčšiu flexibilitu, umožňujúc vykonávanie dodatočej logiky (napríklad logovanie) pred presmerovaním alebo spracovaním.