Best way to catch all unhandled exceptions in all Tomcat (GWT) servlets

Hollerweger picture Hollerweger · Dec 27, 2012 · Viewed 11.4k times · Source

I use Tomcat 7 and Lo4j for all my server logs and GWT for client (only AJAX calls). All my unhandled exceptions get logged in my catalina.log.

Now I want to catch all exceptions and add some of the user's specific Tomcat SessionData.

There are several ways:

  • Try catch over all servlets (there must be a better solution).
  • http://tomcat.apache.org/tomcat-7.0-doc/aio.html: I would have to change my connecter, and I don't know if I could use the Tomcat Session within the Event Handler (EventType.ERROR).
  • Better way?

What would be the best way to achieve this?

Answer

Lyubomyr Shaydariv picture Lyubomyr Shaydariv · Jan 12, 2013

Out of what I understood from your question, you can try to use at least one of two ways:

Basic Logging Servlet

If you have access to the source code of all of your servlets, you can make a little refactoring using a basic super-servlet that is responsible for the logging/whatever of every request working transparently with AJAX, no error forwards directives, and no global exception handlers. Suppose you use service(ServletRequest,ServletResponse) as the servlet entry point (but you can do the following for every do*() method either), then you can create an abstract super-servlet and simply inherit your servlets from it.

<servlet>
    <servlet-name>servlet1</servlet-name>
    <servlet-class>stackoverflow.Servlet1</servlet-class>
</servlet>

<servlet>
    <servlet-name>servlet2</servlet-name>
    <servlet-class>stackoverflow.Servlet2</servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>servlet1</servlet-name>
    <url-pattern>servlet1</url-pattern>
</servlet-mapping>

<servlet-mapping>
    <servlet-name>servlet2</servlet-name>
    <url-pattern>servlet2</url-pattern>
</servlet-mapping>
public abstract class BasicServlet extends HttpServlet {

    /**
     * Won't let it be {@code abstract} - we don't want to force any sub-servlet to implement this method.
     */
    protected void doService(ServletRequest request, ServletResponse response) {
    }

    @Override
    public final void service(ServletRequest request, ServletResponse response) {
        try {
            doService(request, response);
        } catch ( Throwable ex ) {
            err.println(ex.getMessage());
        }
    }

}
public final class Servlet1 extends BasicServlet {

    @Override
    protected void doService(ServletRequest request, ServletResponse response) {
        out.println("I'm servlet #1");
    }

}
public final class Servlet2 extends BasicServlet {

    @Override
    protected void doService(ServletRequest request, ServletResponse response) {
        out.println("I'm servlet #2");
    }

}

An advantage of this method is that you do not need to configure anything else than changing your servlet classes and not depend on the external configuration or context. The disadvantage is that you always must extend BasicServlet.

Filter

I didn't actually test it right now, for more information please see http://docs.oracle.com/javaee/6/api/javax/servlet/Filter.html . Filters allow to intercept each request (we use such a filter implementation for our JSPs while debugging and writing the exceptions into the common log file). The disadvantage is that it's not guaranteed that the filter can cover every exception/case, for example if any filter preceded your own filter.

<filter>
    <filter-name>exceptionLoggingFilter</filter-name>
    <filter-class>stackoverflow.ExceptionLoggingFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>exceptionLoggingFilter</filter-name>
    <url-pattern>*</url-pattern> <!-- we will process every request -->
</filter-mapping>
public final class ExceptionLoggingFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) {
        try {
            filterChain.doFilter(request, response);
        } catch ( Throwable ex ) {
            err.println(ex);
        }
    }

    @Override
    public void destroy() {
    }

}

Hope this helps.