6

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?

Hollerweger
  • 975
  • 1
  • 13
  • 32

3 Answers3

7

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.

Lyubomyr Shaydariv
  • 20,327
  • 12
  • 64
  • 105
  • Thank you for your detailed answer. Unfortunately i can't change the services names they already override gwt [link](http://google-web-toolkit.googlecode.com/svn/javadoc/1.6/com/google/gwt/user/client/rpc/RemoteService.html)'RemoteService' – Hollerweger Jan 13 '13 at 16:55
  • I tried it this way: `@Override public final void service(ServletRequest request, ServletResponse response) { try { super.service(request, response); } catch (Throwable ex) { ServerLog.error(ex.getMessage(), ex); } } but the Exceptiones arn't cought.` – Hollerweger Jan 13 '13 at 16:56
  • @Holla, hm, ok, and what if you try to override the family of the `do***()` methods (`doGet()`, `doHead()`, etc)? – Lyubomyr Shaydariv Jan 13 '13 at 17:06
  • + you might also try to experiment with `service(HttpServletRequest, HttpServletResponse)` rather than `service(ServletRequest, ServletResponse)`. – Lyubomyr Shaydariv Jan 13 '13 at 17:12
  • i foud out that gwt catches all Exceptions but now i found a way that works: '@Override protected void doUnexpectedFailure(Throwable t) { t.printStackTrace(System.err); super.doUnexpectedFailure(t); }' Thanks again – Hollerweger Jan 13 '13 at 17:58
3

Just Overriding the GWT function doUnexpectedFailure worked.

@Override
protected void doUnexpectedFailure(Throwable t) {
  ServerLog.error(t.getMessage(), t);
  super.doUnexpectedFailure(t);
}
Hollerweger
  • 975
  • 1
  • 13
  • 32
  • If you still have access to your application, can you copy what you have in web.xml in your deploy directory and paste it in your answer here? – JustBeingHelpful Oct 12 '15 at 16:12
2

1) You can define error page for your webapp, like this:

<error-page>
        <exception-type>java.lang.Throwable</exception-type>
        <location>/error</location>
</error-page>

Then you can bind another servlet at /error and handle the exception there.

2) You can setUncaughtExceptionHandler for every HTTP connector thread. You could use this technique with servlet filter which would contain reference to the current HttpRequest (say, via a thread local). This won't work with async I/O by the way.

mindas
  • 26,463
  • 15
  • 97
  • 154
  • With the first option, any `Throwable` caught by Tomcat internals redirects to `/error`? – Cratylus Dec 27 '12 at 14:30
  • I would assume any unhandled exception would make it redirect to /error. – mindas Dec 27 '12 at 14:38
  • i will test the 1. option soon. Does it work with Ajax calls and will I be able to get the user's specific Tomcat SessionData? – Hollerweger Dec 29 '12 at 14:04
  • Not sure. It would work from the server side because AJAX requests are HTTP requests. But I haven't tried if client would redirect you to the error page upon AJAX response. But you can always define custom Javascript logic on AJAX response. Anyway, test and let us know ;-) – mindas Dec 29 '12 at 14:56