5

Problem description

Tomcat is logging a SEVERE message including a stacktrace when my HttpServlet is throwing a ServletException, although it is properly re-directed to another HttpServlet in the web.xml.

Tomcat logs the following message with stacktrace:

21-Mar-2015 15:24:57.521 SEVERE [http-nio-8080-exec-28] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [MyHttpServlet] in context with path [/HttpServletExceptionHandler] threw exception [CustomException] with root cause CustomException
at MyHttpServlet.doGet(MyHttpServlet.java:20)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

What did I do?

First, MyHttpServlet throws a ServletException wrapping a CustomException (subclass of Exception) in it's doGet() method:

public class MyHttpServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
        throw new ServletException(new CustomException());
    }

}

Then, the thrown CustomException is re-directed to MyServletExceptionHandler (mapped to location '/MyServletExceptionHandler'. This re-direction is defined in the following manner in the web.xml:

<error-page>
    <exception-type>CustomException</exception-type>
    <location>/MyServletExceptionHandler</location>
</error-page>

Finally, MyServletExceptionHandler receives the thrown exception and prints it:

public class MyServletExceptionHandler extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
        final Throwable throwable = (Throwable) req.getAttribute("javax.servlet.error.exception");
        System.out.println("MyServletExceptionHandler caught Throwable: " + throwable.toString());
    }

}

This results in the expected 'MyServletExceptionHandler caught Throwable: CustomException' print so this does work, but somehow Tomcat also logs the SEVERE message mentioned above, including that stacktrace. This messes up my logging.

Why do I want it this way?

According to Java Beat's OCEJWCD 6 Mock Exam – 4 the above mentioned method is the proper way to deal with Exception handling in Servlets. Question 29 states (spoiler alert: bold are correct answers):

Which of the following is a sensible way of sending an error page to the client in case of a business exception that extends from java.lang.Exception?

  1. Catch the exception and use RequestDispatcher to forward the request to the error page
  2. Don’t catch the exception and define the ‘exception to error-page’ mapping in web.xml
  3. Catch the exception, wrap it into ServletException and define the ‘business exception to error-page’ mapping in web.xml
  4. Catch the exception, wrap it into ServletException, and define the ‘ServletException to error-page’ mapping in web.xml
  5. Don’t do anything, the servlet container will automatically send a default error page

The third answer (which is marked as correct) clearly states that my way of re-directing the exceptions is a sensible solution.

Further discussion material

I found the following quote on this page (from 10-2-2012 by Tom Holloway at CodeRanch.com)

Actually, a ServletException has nowhere to go uphill in a webapp, and therefore having it appear on the master console isn't really that unreasonable, because it indicates that the application isn't handling the problem itself.

In fact, the Javadocs say this about the ServletException constructor:

"Constructs a new servlet exception with the specified message. The message can be written to the server log and/or displayed for the user."

Note that it explicitly says server log.

The server can get involved in a number of ways here. First, you should be able to define a general exception handler in web.xml to permit the app to deal with the exception, where that handler can not only log to the application log, but can determine what, if any, recovery action should be taken (something that the more generic server code cannot do). Secondly, you can define a custom error page, in which case Tomcat will catch the ServletException and dispatch that page. Note, however that the operative word is page. Like login screens, these pages are invoked directly from Tomcat, and therefore cannot be routed through servlets. In other words, use HTML or JSP, not Struts or JSF.

Bottom line, though, is that throwing ServletExceptions is a sign of bad application design. It means that someone was too lazy or too rushed to properly deal with a problem. Compared to that, the location where the error is logged is of secondary importance.

This statement makes me question the Java Beat's OCEJWCD Mock Exam (mentioned above) and my own solution as good practice. Do you think business exceptions should be handled by another Servlet? And if so, do you think that the Servlet Container (Tomcat) should log the stacktrace of these Exceptions or not? If not, what would then be the best practice?

Final remarks

  • Throwing RuntimeExceptions instead of ServletExceptions results in the same SEVERE log.
  • A working example of the problem is provided via this Bitbucket repository.
Community
  • 1
  • 1
Korthout
  • 371
  • 3
  • 12
  • @Klikodid you solved your problem? – erhun Jun 28 '14 at 14:24
  • Nope, thanks for trying to help though – Korthout Jun 28 '14 at 19:21
  • @Kliko Don't edit your post to "bump" it. Earn the needed reputation to post a [bounty on your question](http://stackoverflow.com/help/bounty) to gain attention. – Taryn Jul 23 '14 at 11:16
  • @bluefeet Bumping the question was recommended by this highly-upvoted answer: http://meta.stackexchange.com/a/7054 (I am also slowly building up reputation, but I am finding it hard to answer questions that still require answering. All in due time, I guess.) – Korthout Jul 23 '14 at 13:31
  • 1
    The problem was with your final edit which consisted of `EDIT:BUMP` that is inappropriate. – Taryn Jul 23 '14 at 13:32
  • 1
    @Kliko - when I look at the question as a whole, its a bit large and hard to follow. It rambles on with no real structure. I tried reading it and lost all interest. I can barely make out the problem statement. You also don't provide the code that's causing an exception (rather, it rambles about other things, like docs and blog posts about how things are supposed to work). You might consider one last edit and contract it to the necessary information needed to help. Its up for a Close at the moment, and I'm inclined to agree - *Unclear what you're asking* and *Requires minimal example*. – jww Jul 28 '14 at 11:51
  • 1
    @jww I completely agree. The issue started small, but as I progressed to read more and more about the subject I tried to add my findings as status updates. As soon as I have some time, I will try to combine all information into a minimised version of the problem at hand, although an alternative solution has already been provided. So, I will clean-up this question as soon as possible. Thank you for your time. – Korthout Jul 28 '14 at 15:23

1 Answers1

1

It seems your basic problem is that you want to centralize your error handling but without using a mechanism that causes Tomcat to log the errors as SEVERE?

Since you control all the servlets AFAICT from your question would it not make more sense to define an abstract base servlet that defines all the error handling logic and then just have the rest of your servlets derive from this class?

So you have an abstract base servlet:

public abstract class MyServletBase extends HttpServlet {

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) {
    try {
      doGetInternal(req, resp);
    } catch (RuntimeException e) {
      handleError(e, req, resp);
    }
  }

  protected void handleError(RuntimeException e, HttpServletRequest req, HttpServletResponse resp) {
    // Error handling logic goes here
  }

  protected void doGetInternal(HttpServletRequest req, HttpServletResponse resp);

}

And then your actual servlet:

public class MyServlet extends MyServletBase {

  @Override
  protected void doGetInternal(HttpServletRequest req, HttpServlet resp) {
    // Actual servlet logic here
  }
}

This is a rough sketch off the top of my head with no reference to the Javadoc so may not have the method signatures perfect but hopefully you get the idea.

It also has the advantage that if you ever need to add extra error handling logic to a derived servlet and you don't want to change the base servlet for whatever reason you can just override the handleError() method which is not something you can so easily do when using Tomcat's exception handler mechanism

RobV
  • 28,022
  • 11
  • 77
  • 119
  • This is a very useful answer. I will try to implement it as soon as possible. This pattern seems way more natural for exception handling than Tomcat's exception handler mechanism, especially when using the overriding of the base handleError() method, as you described. As soon as I have some results I will come back to check this answer as my accepted answer. Thank you. – Korthout Jul 28 '14 at 15:17